Реализация компонента инпута с плавающим лейблом

Попытался реализовать компонент инпута с плавающим лейблом (Floating Label). Код компонента FloatingInput.vue:

<script setup>
import { computed, ref } from 'vue'

const props = defineProps({
  modelValue: String,
  label: String,
  type: {
    type: String,
    default: 'text',
  },
})

const emit = defineEmits(['update:modelValue'])

const isFocused = ref(false)
const isFocusedOrText = computed(() => isFocused.value || !!props.modelValue.trim())

const handleInput = (e) => {
  emit('update:modelValue', e.target.value)
}
</script>

<template>
  <div :class="$style.form_group">
    <input
      :type="type"
      :class="[$style.form_group_input, { [$style.form_group_input_active]: isFocused }]"
      @focus="isFocused = true"
      @blur="isFocused = false"
      @input="handleInput"
    />
    <label
      :class="[$style.form_group_label, { [$style.form_group_label_active]: isFocusedOrText }]"
      >{{ props.label }}</label
    >
  </div>
</template>

<style module>
.form_group {
  position: relative;
  flex: 1 0 260px;
  block-size: max-content;
}

.form_group_input {
  block-size: 100%;
  inline-size: 100%;
  padding: 26px 16px 8px;
  border: none;
  outline: none;
  border-radius: var(--nd-border-radius-small);
  color: var(--nd-color-light);
  background-color: transparent;
  box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.5);
  transition-duration: var(--nd-transition-duration);
}

.form_group_input_active {
  box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 1);
}

.form_group_label {
  position: absolute;
  top: 50%;
  left: 16px;
  transform: translateY(-53%);
  transition-duration: var(--nd-transition-duration);
  pointer-events: none;
}

.form_group_label_active {
  transform: translate(0, -20px);
  font-size: 14px;
}
</style>

Пример использования в <template> внутри HomePage.vue<script> всё необходимое создано и импортировано, email и nickname — реактивные поля):

<div :class="$style.fluid_form_body">
  <FloatingInput v-model="email" label="Электронная почта" type="email" />
  <FloatingInput v-model="nickname" label="Никнейм" />
</div>

В принципе сам код работает, но выглядит каким-то громоздким. Есть ли какие-то советы по улучшению качества кода, оптимизации? В этом проекте впервые работал с модульными стилями и с defineEmits.

Так выглядят инпуты на сайте, в форме


Ответы (0 шт):