Select 选择器

显示一个选项列表供用户选择——由按钮触发。复制了 shadcn-vue Select 组件的核心能力,支持单选、多选、搜索、分组和其他功能。基于 BrConfigProvider 进行全局主题配置。

组件特性

  • 单选和多选:支持单选和多选模式。
  • 🔍 搜索支持:支持本地和远程搜索过滤。
  • 📦 分组支持:通过 BrSelectGroup 支持选项分组。
  • 可清除:支持 clearable 属性清除选择。
  • 📏 多种尺寸:支持 smmdlg 尺寸规格。
  • ⚠️ 错误状态:支持 error 属性显示错误状态。
  • 🎨 主题定制:基于 BrConfigProvider 支持全局主题配置和 TailwindCSS 局部覆盖。

基础用法

最基本的单选模式,支持自定义占位符。

<script setup lang="ts">
import { BrSelect } from '@breezeui/vue'
import { ref } from 'vue'

const value = ref('apple')
const options = [
  { label: 'Apple', value: 'apple' },
  { label: 'Banana', value: 'banana' },
  { label: 'Orange', value: 'orange' },
  { label: 'Blueberry', value: 'blueberry' },
  { label: 'Grapes', value: 'grapes' },
  { label: 'Pineapple', value: 'pineapple' },
]
</script>

<template>
  <div class="flex flex-col gap-4 w-[280px]">
    <BrSelect 
      v-model="value" 
      :options="options" 
      placeholder="Select a fruit" 
    />
    <div class="text-sm text-muted-foreground">
      Selected: {{ value }}
    </div>
  </div>
</template>

分组

使用 group 字段将选项分组显示。

<script setup lang="ts">
import { BrSelect } from '@breezeui/vue'
import { ref } from 'vue'

const value = ref('')
const options = [
  { label: 'Apple', value: 'apple', group: 'Fruits' },
  { label: 'Banana', value: 'banana', group: 'Fruits' },
  { label: 'Blueberry', value: 'blueberry', group: 'Fruits' },
  { label: 'Carrot', value: 'carrot', group: 'Vegetables' },
  { label: 'Potato', value: 'potato', group: 'Vegetables' },
  { label: 'Broccoli', value: 'broccoli', group: 'Vegetables' },
  { label: 'Beef', value: 'beef', group: 'Meat' },
  { label: 'Chicken', value: 'chicken', group: 'Meat' },
]
</script>

<template>
  <div class="w-[280px]">
    <BrSelect 
      v-model="value" 
      :options="options" 
      placeholder="Select food" 
    />
  </div>
</template>

远程搜索

启用 filterable 属性并监听 search 事件以实现远程搜索。支持 loading 状态。

<script setup lang="ts">
import { BrSelect } from '@breezeui/vue'
import { ref, watch } from 'vue'

const value = ref('')
const loading = ref(false)
const options = ref<{ label: string; value: string }[]>([])

// Simulate remote search
const handleSearch = (query: string) => {
  if (!query) {
    options.value = []
    return
  }
  
  loading.value = true
  // Mock API call
  setTimeout(() => {
    options.value = [
      { label: query + ' Option 1', value: query + '-1' },
      { label: query + ' Option 2', value: query + '-2' },
      { label: query + ' Option 3', value: query + '-3' },
    ]
    loading.value = false
  }, 1000)
}
</script>

<template>
  <div class="w-[280px]">
    <BrSelect 
      v-model="value" 
      :options="options" 
      filterable 
      :loading="loading" 
      placeholder="Type to search..." 
      empty-text="No results found."
      @search="handleSearch"
    />
    <div class="mt-4 text-sm text-muted-foreground">
      Current Value: {{ value }}
    </div>
  </div>
</template>

多选与可清除

启用 multiple 属性以支持多选,启用 clearable 以支持一键清除。

<script setup lang="ts">
import { BrSelect } from '@breezeui/vue'
import { ref } from 'vue'

const value = ref(['react', 'vue'])
const options = [
  { label: 'Vue', value: 'vue' },
  { label: 'React', value: 'react' },
  { label: 'Angular', value: 'angular' },
  { label: 'Svelte', value: 'svelte' },
  { label: 'Solid', value: 'solid' },
]
</script>

<template>
  <div class="flex flex-col gap-4 w-[350px]">
    <BrSelect 
      v-model="value" 
      :options="options" 
      multiple 
      clearable 
      placeholder="Select frameworks" 
    />
    <div class="text-sm text-muted-foreground">
      Selected: {{ value }}
    </div>
  </div>
</template>

尺寸

支持 default, sm, lg 尺寸。

<script setup lang="ts">
import { BrSelect } from '@breezeui/vue'
import { ref } from 'vue'

const value = ref('apple')
const options = [
  { label: 'Apple', value: 'apple' },
  { label: 'Banana', value: 'banana' },
  { label: 'Orange', value: 'orange' },
]
</script>

<template>
  <div class="flex flex-col gap-4 w-full max-w-sm">
    <div class="grid gap-2">
      <label class="text-sm font-medium">Extra Small Size (xs)</label>
      <BrSelect v-model="value" :options="options" size="xs" />
    </div>

    <div class="grid gap-2">
      <label class="text-sm font-medium">Small Size (sm)</label>
      <BrSelect v-model="value" :options="options" size="sm" />
    </div>

    <div class="grid gap-2">
      <label class="text-sm font-medium">Default Size</label>
      <BrSelect v-model="value" :options="options" />
    </div>

    <div class="grid gap-2">
      <label class="text-sm font-medium">Medium Size (md)</label>
      <BrSelect v-model="value" :options="options" size="md" />
    </div>
    
    <div class="grid gap-2">
      <label class="text-sm font-medium">Large Size (lg)</label>
      <BrSelect v-model="value" :options="options" size="lg" />
    </div>

    <div class="grid gap-2">
      <label class="text-sm font-medium">Extra Large Size (xl)</label>
      <BrSelect v-model="value" :options="options" size="xl" />
    </div>

    <div class="grid gap-2">
      <label class="text-sm font-medium">2X Large Size (2xl)</label>
      <BrSelect v-model="value" :options="options" size="2xl" />
    </div>
  </div>
</template>

状态

支持 error 状态。

<script setup lang="ts">
import { BrSelect } from '@breezeui/vue'
import { ref } from 'vue'

const value = ref('apple')
const options = [
  { label: 'Apple', value: 'apple' },
  { label: 'Banana', value: 'banana' },
  { label: 'Orange', value: 'orange' },
]
</script>

<template>
  <div class="flex flex-col gap-4 w-full max-w-sm">
    <div class="grid gap-2">
      <label class="text-sm font-medium">Error State</label>
      <BrSelect v-model="value" :options="options" error />
    </div>
  </div>
</template>

主题定制

BrSelect 组件完全遵循 BrConfigProvider 的主题规范。

全局配置与局部覆盖

  • 全局配置:通过 BrConfigProvidertheme 属性配置全局颜色,Select 组件将自动适配。
  • 局部覆盖:你可以使用 Tailwind CSS 类名直接覆盖样式。
<script setup lang="ts">
import { BrSelect, BrConfigProvider } from '@breezeui/vue'
import { ref } from 'vue'

const value = ref('light')
const options = [
  { label: 'Light Mode', value: 'light' },
  { label: 'Dark Mode', value: 'dark' },
  { label: 'System', value: 'system' },
]

// Custom theme configuration
const customTheme = {
  background: '222.2 84% 4.9%',
  foreground: '210 40% 98%',
  popover: '222.2 84% 4.9%',
  popoverForeground: '210 40% 98%',
  primary: '210 40% 98%',
  primaryForeground: '222.2 47.4% 11.2%',
  border: '217.2 32.6% 17.5%',
  input: '217.2 32.6% 17.5%',
  ring: '212.7 26.8% 83.9%',
}
</script>

<template>
  <div class="space-y-8">
    <!-- Default Theme -->
    <div class="space-y-2">
      <h3 class="text-sm font-medium">Default Theme</h3>
      <div class="w-[280px]">
        <BrSelect v-model="value" :options="options" />
      </div>
    </div>

    <!-- Custom Dark Theme via ConfigProvider -->
    <div class="space-y-2">
      <h3 class="text-sm font-medium">Custom Dark Theme (via ConfigProvider)</h3>
      <BrConfigProvider :theme="customTheme" class="p-6 rounded-lg border bg-background text-foreground">
        <div class="w-[280px]">
          <BrSelect v-model="value" :options="options" />
        </div>
      </BrConfigProvider>
    </div>
  </div>
</template>

API

Props

属性类型默认值说明
modelValuestring | number | (string | number)[]-绑定值
options(string | number | SelectOption)[][]选项数据
placeholderstring'Select an option'占位符
disabledbooleanfalse是否禁用
clearablebooleanfalse是否可清除
filterablebooleanfalse是否可搜索
multiplebooleanfalse是否多选
loadingbooleanfalse是否加载中(用于远程搜索)
emptyTextstring'No options found.'当未找到选项时显示的文本
size'default' | 'sm' | 'lg''default'尺寸
errorbooleanfalse是否处于错误状态

SelectOption 类型

interface SelectOption {
  label: string
  value: string | number
  disabled?: boolean
  group?: string
}

Events

名称说明参数
update:modelValue当绑定值变化时触发value: string | number | (string | number)[]
change当选中值变化时触发value: string | number | (string | number)[]
clear当点击清除按钮时触发-
search当搜索输入变化时触发query: string