WithFloatingLabel

View source
带浮动标签和清除按钮的输入框组件。

简介

MWithFloatingLabel 是一个带有浮动标签效果的输入框组件。标签在输入框为空时居中显示为占位符,聚焦或有内容时自动上浮,同时内置清除按钮,有内容时右侧自动显示。

基于 Nuxt UI 的 Input 组件封装

用法

标签在空值时居中显示,聚焦或有内容时自动上浮:

<script setup lang="ts">
const value = ref("test@example.com")
</script>

<template>
  <MWithFloatingLabel v-model="value" label="邮箱地址" />
</template>

leadingIcon 前置图标

通过 leadingIcon 为输入框添加前置图标:

<template>
  <MWithFloatingLabel label="用户名" leading-icon="i-lucide-user" />
</template>

size 尺寸

通过 size 切换输入框与标签尺寸:

<template>
  <MWithFloatingLabel label="尺寸演示" size="md" />
</template>

clearButtonProps 清除按钮

<script setup lang="ts">
const value = ref('test@example.com')
</script>

<template>
  <MWithFloatingLabel
    v-model="value"
    label="邮箱地址"
    :ui="{ label: 'text-warning' }"
    :clear-button-props="{ color: 'error', icon: 'i-lucide-x' }"
  />
</template>

示例

清除事件

通过 @clear 事件监听清除操作:

<script setup lang="ts">
const toast = useToast()
const email = ref('user@example.com')

function handleClear() {
  toast.add({
    title: '已清除',
    description: '输入内容已清空',
    color: 'neutral'
  })
}
</script>

<template>
  <MWithFloatingLabel v-model="email" label="邮箱地址" leading-icon="i-lucide-mail" @clear="handleClear" />
</template>

API

Props

Prop Default Type
as'div'any

The element or component this component should render as.

labelstring
size"xs" | "sm" | "md" | "lg" | "xl"
clearButtonPropsButtonProps
idstring
namestring
type"number" | "color" | "button" | "checkbox" | "date" | "datetime-local" | "email" | "file" | "hidden" | "image" | "month" | "password" | "radio" | "range" | "reset" | "search" | "submit" | "tel" | "text" | "time" | "url" | "week" | string & {}
placeholderstring

The placeholder text when the input is empty.

color'primary'"primary" | "secondary" | "info" | "success" | "warning" | "error" | "important" | "neutral"
variant'outline'"outline" | "soft" | "subtle" | "ghost" | "none"
autocompletestring & {} | "on" | "off"
autofocusDelaynumber
defaultValueT
modelModifiersModelModifiers
iconany

Display an icon based on the leading and trailing props.

avatarAvatarProps

Display an avatar on the left side.

leadingIconany

Display an icon on the left side.

trailingIconany

Display an icon on the right side.

loadingIconappConfig.ui.icons.loadingany

The icon when the loading prop is true.

liststring
maxstring | number
maxlengthstring | number
minstring | number
minlengthstring | number
patternstring
readonlyfalse | true | "true" | "false"
stepstring | number
modelValueT
requiredboolean
autofocusboolean
disabledboolean
highlightboolean

Highlight the ring color like a focus state.

fixedboolean

Keep the mobile text size on all breakpoints.

leadingboolean

When true, the icon will be displayed on the left side.

trailingboolean

When true, the icon will be displayed on the right side.

loadingboolean

When true, the loading icon will be displayed.

uiRecord<string, ClassNameValue> & { root?: SlotClass; base?: SlotClass; leading?: SlotClass; leadingIcon?: SlotClass; leadingAvatar?: SlotClass; leadingAvatarSize?: SlotClass; trailing?: SlotClass; trailingIcon?: SlotClass; label?: SlotClass; labelText?: SlotClass; }

Emits

Event Type
update:modelValue[value: T]
blur[event: FocusEvent]
change[event: Event]
clear[]

Slots

Slot Type
leading{ ui: { root: (props?: Record<string, any>) => string; base: (props?: Record<string, any>) => string; leading: (props?: Record<string, any>) => string; leadingIcon: (props?: Record<string, any>) => string; leadingAvatar: (props?: Record<string, any>) => string; leadingAvatarSize: (props?: Record<string, any>) => string; trailing: (props?: Record<string, any>) => string; trailingIcon: (props?: Record<string, any>) => string; }; }

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    withFloatingLabel: {
      slots: {
        base: 'peer',
        trailing: 'pe-1',
        label: 'pointer-events-none absolute -top-2.5 px-1.5 transition-all text-highlighted text-xs font-medium peer-focus:-top-2.5 peer-focus:text-highlighted peer-focus:text-xs peer-focus:font-medium peer-placeholder-shown:text-dimmed peer-placeholder-shown:font-normal peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 peer-focus:translate-y-0',
        labelText: 'inline-flex bg-default px-1'
      },
      variants: {
        size: {
          xs: {
            label: 'peer-placeholder-shown:text-xs'
          },
          sm: {
            label: 'peer-placeholder-shown:text-xs'
          },
          md: {
            label: 'peer-placeholder-shown:text-sm'
          },
          lg: {
            label: 'peer-placeholder-shown:text-sm'
          },
          xl: {
            label: 'peer-placeholder-shown:text-base'
          }
        },
        hasLeading: {
          true: {},
          false: {
            label: 'left-0'
          }
        }
      },
      compoundVariants: [
        {
          size: 'xs',
          hasLeading: true,
          class: {
            label: 'left-5'
          }
        },
        {
          size: 'sm',
          hasLeading: true,
          class: {
            label: 'left-6'
          }
        },
        {
          size: 'md',
          hasLeading: true,
          class: {
            label: 'left-7'
          }
        },
        {
          size: 'lg',
          hasLeading: true,
          class: {
            label: 'left-8'
          }
        },
        {
          size: 'xl',
          hasLeading: true,
          class: {
            label: 'left-9'
          }
        }
      ],
      defaultVariants: {
        size: 'md',
        hasLeading: false
      }
    }
  }
})

Changelog

No recent changes
Copyright © 2025 - 2026 YiXuan - MIT License