Files
Kotyata/apps/client/components/network-select.vue
2026-03-17 13:24:22 +03:00

184 lines
3.3 KiB
Vue

<template>
<div
class="network-select network-select--secondary network-select--large"
:class="{
'has-value': !!field.value.value,
'is-disabled': disabled,
'is-invalid': invalid,
}"
v-bind="$attrs"
@click="show"
>
<label
ref="wrapper"
class="network-select__wrapper"
tabindex="0"
@keydown.enter="show"
@keydown.space="show"
>
<span class="network-select__content">
<div
v-if="!!field.value.value"
class="network-select__value"
>
<UiCoin
:code="assetCode"
class="network-select__coin"
/>
<span>{{ modelValue }}</span>
</div>
<p class="network-select__label">{{ $t('network') }}</p>
<UiButton
class="network-select__action"
type="ghost"
size="small"
disabled
>
{{ actionText }}
</UiButton>
</span>
</label>
<div
v-if="invalid"
class="network-select__bottom"
>
<div class="network-select__validation-message">
{{ field.errorMessage.value }}
</div>
</div>
</div>
</template>
<script setup>
import { computed, ref, toRef, watch } from 'vue'
import { useField } from 'vee-validate'
const props = defineProps({
id: {
type: String,
required: true,
},
assetCode: {
type: String,
required: true,
},
modelValue: {
type: String,
},
disabled: {
type: Boolean,
default: false,
},
})
const id = toRef(props, 'id')
const { t } = useI18n()
const field = useField(id, 'required', {
validateOnValueUpdate: false,
syncVModel: true,
initialValue: !isEmptyValue(props.modelValue) ? props.modelValue : undefined,
})
const wrapper = ref()
const active = ref(false)
const invalid = computed(() => !props.disabled && !!field.errorMessage.value)
const actionText = computed(() =>
field.value.value ? t('change') : t('select'),
)
watch(field.value, hide)
watch(active, (value) => {
if (!value)
wrapper.value?.focus()
})
function show() {
active.value = true
}
function hide() {
active.value = false
}
function isEmptyValue(value) {
return [null, undefined, ''].includes(value)
}
</script>
<style lang="scss">
.network-select {
$self: &;
&__wrapper {
display: block;
border-radius: 12px;
outline: 1px solid $clr-grey-300;
outline-offset: -1px;
padding-inline: 16px;
background-color: $clr-white;
height: 48px;
}
&__content {
position: relative;
display: flex;
align-items: center;
height: 100%;
}
&__label {
@include txt-i-m;
position: absolute;
pointer-events: none;
top: 15px;
left: 0;
color: $clr-grey-400;
transform-origin: 0 0;
#{$self}.has-value & {
transform: translateY(-7px) scale(0.78);
color: $clr-grey-500;
}
}
&__value {
@include txt-i-m;
display: flex;
align-items: center;
gap: 4px;
padding-block: 22px 8px;
flex: 1;
}
&__coin {
--coin-size: 16px;
--coin-border-radius: 3px;
}
&__action {
margin-left: auto;
}
&__bottom {
@include txt-s-m;
margin-top: 4px;
padding-inline: 16px;
}
&__validation-message {
color: var(--input-validation-message-color);
}
}
</style>