# Suggest

搜索建议组件，支持通过异步搜索获取选项并选择。提供单选（Suggest）和多选（MultiSuggest）两种模式。

## 适用场景

- 需要通过输入关键词异步搜索并从结果中选择的场景
- 远程数据源的下拉选择（选项不固定，需要实时查询）
- 需要自定义选项展示内容的搜索选择
- 多选模式下通过搜索逐一添加选项

## Props

### Suggest Props

| 名称 | 类型 | 默认值 | 必填 | 说明 |
|------|------|--------|------|------|
| className | `string` | `-` | 否 | 自定义 class |
| value | `SuggestValue` | `-` | 否 | 选中控制值 |
| defaultValue | `SuggestValue` | `-` | 否 | 选中默认值 |
| disabled | `boolean` | `false` | 否 | 是否禁用 |
| placeholder | `string` | `-` | 否 | 未选择占位提示 |
| size | `"sm" \| "md" \| "lg" \| "xl"` | `-` | 否 | 尺寸 |
| placement | `Placement` | `"bottom-start"` | 否 | 选项列表弹出位置 |
| onChange | `(value: SuggestValue) => void` | `-` | 否 | 值变化时的回调 |
| onSearch | `(query: string) => Promise<SuggestOption[]>` | `-` | 否 | 查询回调 |
| onKeyDown | `(e: KeyboardEvent<Element>) => void` | `-` | 否 | 键盘按下回调 |
| loadingDelay | `number` | `300` | 否 | loading 图标的延迟响应时间（ms），0 为立即显示 |
| trim | `boolean` | `true` | 否 | 是否使用 trimmed 过的 inputValue 作为 query 参数 |
| shouldEmptySearch | `boolean` | `false` | 否 | 空字符串是否进行 search，true 则在 focus 状态下亦会触发 |
| status | `"normal" \| "error"` | `"normal"` | 否 | 控件框状态 |
| fixDropdownWidth | `boolean` | `-` | 否 | 将选项列表的宽度固定为 selection 宽度 |
| suffixIcon | `FC<{}>` | `SvgSearch` | 否 | 后缀图标，falsy 则显示为下拉箭头 |
| noOptionsHint | `ReactNode \| ((input: string) => ReactNode)` | `-` | 否 | 无选项提示内容 |

### MultiSuggest Props

| 名称 | 类型 | 默认值 | 必填 | 说明 |
|------|------|--------|------|------|
| className | `string` | `""` | 否 | 自定义 class |
| value | `MultiSuggestValue` | `-` | 否 | 选中控制值 |
| defaultValue | `MultiSuggestValue` | `-` | 否 | 选中默认值 |
| onChange | `(value: MultiSuggestValue) => void` | `-` | 否 | 值变化时的回调 |
| withCount | `boolean` | `-` | 否 | 是否显示计数 |
| maxNumber | `number` | `-` | 否 | 最大选择个数 |
| size | `"sm" \| "md" \| "lg" \| "xl"` | 取 config context `baseSize` | 否 | 尺寸 |
| disabled | `boolean` | `-` | 否 | 是否禁用 |
| placeholder | `string` | `-` | 否 | 未选择占位提示 |
| placement | `Placement` | `-` | 否 | 选项列表弹出位置 |
| onSearch | `(query: string) => Promise<SuggestOption[]>` | `-` | 否 | 查询回调 |
| onKeyDown | `(e: KeyboardEvent<Element>) => void` | `-` | 否 | 键盘按下回调 |
| loadingDelay | `number` | `-` | 否 | loading 图标的延迟响应时间（ms） |
| trim | `boolean` | `-` | 否 | 是否使用 trimmed 过的 inputValue 作为 query 参数 |
| shouldEmptySearch | `boolean` | `-` | 否 | 空字符串是否进行 search |
| status | `"normal" \| "error"` | `-` | 否 | 控件框状态 |
| fixDropdownWidth | `boolean` | `-` | 否 | 将选项列表的宽度固定为 selection 宽度 |
| suffixIcon | `FC<{}>` | `null` | 否 | 后缀图标，falsy 则显示为下拉箭头 |
| noOptionsHint | `ReactNode \| ((input: string) => ReactNode)` | `-` | 否 | 无选项提示内容 |

### SuggestOption Props

| 名称 | 类型 | 默认值 | 必填 | 说明 |
|------|------|--------|------|------|
| id | `MenuItemId` | `-` | 是 | 唯一标识 |
| label | `string` | `-` | 是 | 显示文字，用于已选择展示 |
| content | `ReactNode` | `-` | 否 | 显示内容，用于选项，不指定则使用 label |
| disabled | `boolean` | `-` | 否 | 是否禁用 |
| children | `SuggestOption[]` | `-` | 否 | 子选项，只支持一层，用于 group |

## 典型用法

### 基础单选

```tsx
import { useState } from 'react'
import { Suggest, SuggestOption, SuggestValue } from '@befe/brick'

const handleFetch = (inputValue: string): Promise<SuggestOption[]> => new Promise((resolve) => {
    setTimeout(() => {
        resolve(inputValue && inputValue.length < 7 ? [
            { id: `opt_${inputValue}`, label: `option_${inputValue}` },
        ] : [])
    }, 500)
})

function Demo() {
    const [value, setValue] = useState<SuggestValue>(null)

    return (
        <Suggest
            onSearch={handleFetch}
            value={value}
            onChange={setValue}
            placeholder="请搜索"
        />
    )
}
```

### 多选

MultiSuggest 在存在已选值且组件未禁用时，会自动提供整体清除入口，无需额外配置 prop。

```tsx
import { MultiSuggest, MultiSuggestValue } from '@befe/brick'

function Demo() {
    return (
        <div>
            <MultiSuggest onSearch={handleSearch} placeholder="请搜索" shouldEmptySearch />
            <MultiSuggest onSearch={handleSearch} withCount />
            <MultiSuggest onSearch={handleSearch} maxNumber={3} />
        </div>
    )
}
```

### 自定义选项内容

```tsx
import { Suggest, SuggestOption } from '@befe/brick'

const getOptions = (inputValue: string): SuggestOption[] => [
    {
        id: 'option_1', label: 'option_1',
        content: (
            <span>
                <span style={{ color: 'green' }}>code_1</span>
                <span> &gt; </span>
                <span style={{ color: 'red' }}>{inputValue}</span>
                <span> &gt; </span>
                <span>option_1</span>
            </span>
        ),
    },
]

function Demo() {
    return (
        <Suggest
            onSearch={(q) => new Promise(resolve => setTimeout(() => resolve(getOptions(q)), 500))}
        />
    )
}
```

### 多选显示计数与限制上限

```tsx
import { MultiSuggest } from '@befe/brick'

function Demo() {
    return (
        <div>
            <MultiSuggest onSearch={handleSearch} withCount />
            <MultiSuggest onSearch={handleSearch} maxNumber={3} />
        </div>
    )
}
```

## 注意事项

- `SuggestValue` 是 `SuggestOption` shape 的对象（包含 `id` + `label`），而非单一标识值
- 外部设置值时需同时提供 `id` 和 `label`，例如 `setValue({id: 'x', label: 'X'})`
- 自定义扩展 option 时可用 `interface CustomOption extends SuggestOption`，`onChange` 可直接获取到扩展字段
- `SuggestOption` 使用 `id` 作为标识字段（与 Select 的 `value` 不同）
- `props.loadingDelayInMS` 已废弃，改用 `props.loadingDelay`
