This commit is contained in:
Nadar
2026-03-17 13:24:22 +03:00
commit 82e5ac9d81
554 changed files with 29637 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
<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>

View File

@@ -0,0 +1,125 @@
@use '../../styles/variables' as *;
@use '../../styles/mixins' as *;
.ui-search {
$self: &;
&__wrapper {
--border-color: var(--search-border-color);
--border-width: 1px;
display: flex;
align-items: center;
padding: 8px 16px;
outline: var(--border-width) solid var(--border-color);
outline-offset: calc(var(--border-width) * -1);
color: var(--search-color);
border-radius: 12px;
flex: 1;
#{$self}--large & {
height: 48px;
}
#{$self}--small & {
padding: 8px 12px;
border-radius: 8px;
}
#{$self}:not(.is-disabled) & {
cursor: text;
}
#{$self}:not(.is-disabled) &:hover,
#{$self}.is-focused & {
--border-width: 2px;
}
#{$self}.is-disabled & {
--border-color: var(--search-disabled-border-color);
background: var(--search-disabled-background);
}
}
&__icon {
color: var(--search-icon-color);
margin-right: 8px;
pointer-events: none;
transition: color .2s ease-out;
#{$self}--small & {
margin-right: 4px;
}
#{$self}.is-focused & {
color: var(--search-active-icon-color);
}
}
&__input {
@include txt-i-m;
flex: 1;
width: 100%;
height: 100%;
border: none;
background: none;
outline: none;
padding: 0;
color: var(--search-value-color);
caret-color: var(--search-caret-color);
appearance: none;
&::-webkit-search-decoration,
&::-webkit-search-cancel-button,
&::-webkit-search-results-button,
&::-webkit-search-results-decoration {
-webkit-appearance: none;
}
&::-ms-reveal,
&::-ms-clear {
display: none;
}
&:-webkit-autofill {
-webkit-background-clip: text;
-webkit-text-fill-color: var(--search-color);
}
&::placeholder {
color: var(--search-placeholder-color);
}
#{$self}--small & {
@include txt-r-m;
}
#{$self}.is-disabled & {
color: $clr-grey-400;
}
}
&__clear {
margin-left: 8px;
#{$self}--small & {
margin-left: 4px;
}
}
/* prettier-ignore */
@include element-variant(
'search',
'',
(
'border-color': $clr-grey-300,
'color': $clr-black,
'placeholder-color': $clr-grey-400,
'icon-color': $clr-grey-400,
'active-icon-color': $clr-grey-500,
'disabled-background': $clr-grey-100,
'disabled-border-color': $clr-grey-300
)
);
}