# Select

下拉选择

指定列表单个类型时使用

**tips：** 可通过键盘上下键进行列选择，enter 键确认选择。注意，如果 开启`closeOnScroll`，则无法使用

## 案例演示

### Select 基本使用

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

function onChange(value, item){
  console.log(value, item);
}

ReactDOM.render(
  <Select onChange={onChange} defaultValue="1">
    <Option>请选择</Option>
    <Option value="0">张三</Option>
    <Option value="1">李四</Option>
    <Option value="2">王五</Option>
  </Select>, _react_runner_);
```
---demoend


### Select 可控

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

class Demo extends Component {
  state = {
    value: 'ddd'
  };

  onChange = (value) => {
    this.setState({
      value
    });
  }

  render() {
    return (
      <Select value={this.state.value} onChange={this.onChange}>
        <Option>请选择</Option>
        <Option value="0">张三</Option>
        <Option value="1">李四</Option>
        <Option value="2">王五</Option>
      </Select>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

### Select 基本使用 自定义 style

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

function onChange(value, item){
  console.log(value, item);
}

const searchStyle = {
  // 外层样式
  style: {},
  // 输入框样式
  inputStyle: {
    background: 'orange'
  },
  // 取消按钮样式
  btnStyle: {
    color: 'white'
  }
};

ReactDOM.render(
  <Select
    onChange={onChange}
    defaultValue="1"
    searchable
    style={{ background: 'yellow' }}
    popoverStyle={{ background: 'blue' }}
    popoverContentStyle={{ background: 'red' }}
    searchStyle={searchStyle}
  >
    <Option>请选择</Option>
    <Option value="0">张三</Option>
    <Option value="1">李四</Option>
    <Option value="2">王五</Option>
  </Select>, _react_runner_);
```
---demoend

### Select 基本使用 placeholder

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

function onChange(value, item){
  console.log(value, item);
}

ReactDOM.render(
  <Select onChange={onChange} placeholder="请选择人员">
    <Option value="0">张三</Option>
    <Option value="1">李四</Option>
    <Option value="2">王五</Option>
  </Select>, _react_runner_);
```
---demoend

### Select 基本使用 addonIcon

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

function onChange(value, item){
  console.log(value, item);
}

ReactDOM.render(
  <Select onChange={onChange} addonIcon="bars" defaultValue="1">
    <Option value="0">张三</Option>
    <Option value="1">李四</Option>
    <Option value="2">王五</Option>
  </Select>, _react_runner_);
```
---demoend

### Select 基本使用 selectedIcon

通过设置 `selectedIcon` 来实现自定义选中项图标，如果要禁用图标，则可以通过设置 `selectedIcon=null` 来实现。

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

function onChange(value, item){
  console.log(value, item);
}

ReactDOM.render((
  <div>
    <Select onChange={onChange} selectedIcon="collection" defaultValue="1">
      <Option value="0">张三张三张三张三张三张三</Option>
      <Option value="1">李四</Option>
      <Option value="2">王五</Option>
    </Select>
    <Select onChange={onChange} selectedIcon={null} defaultValue="1">
      <Option value="0">张三</Option>
      <Option value="1">李四</Option>
      <Option value="2">王五</Option>
    </Select>
  </div>
), _react_runner_);
```
---demoend

### Select data 数据

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

function onChange(value, item){
  console.log(value, item);
}

const data = [
  { id: 0, name: '张三' },
  { id: 1, name: '李四' },
  { id: 2, name: '王五' }
];

ReactDOM.render(
<Select
  data={data}
  renderOption={item => <Option value={item.id}>{item.name}</Option>}
  defaultOption={<Option>请选择</Option>}
  onChange={onChange}
/>, _react_runner_);
```
---demoend

### Select data 中包含空值

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

function onChange(value, item){
  console.log(value, item);
}

const data = [
  { id: 0, name: '张三' },
  { id: 1, name: '李四' },
  { id: 2, name: '王五' },
  { id: '', name: '空值' }
];

ReactDOM.render(
<Select
  data={data}
  renderOption={item => <Option value={item.id}>{item.name}</Option>}
  onChange={onChange}
/>, _react_runner_);
```
---demoend

### Select data 多处控制

> 其中，第二个 select 未设置 value，仅设置 defaultValue，注意查看区别。

---demo
```js
import { Select, Input } from 'amos-framework';

const Option = Select.Option;

const data = [
  { id: '0', name: '张三' },
  { id: '1', name: '李四' },
  { id: '2', name: '王五' }
];

class Demo extends Component {
  state = {
  	value: '1'
  };

  onChange = (value, item) => {
    console.log(value, item);
    this.setState({
      value
    });
  }

  iptChange = (e) => {
  	this.setState({
    	value: e.target.value
    });
  }

	render(){
    return (
      <div>
        <Input value={this.state.value} onChange={this.iptChange} />
        <Select
          data={data}
          value={this.state.value}
          renderOption={item => <Option value={item.id}>{item.name}</Option>}
          defaultOption={<Option>请选择</Option>}
          onChange={this.onChange}
        />
        <Select
          data={data}
          defaultValue={this.state.value}
          renderOption={item => <Option value={item.id}>{item.name}</Option>}
          defaultOption={<Option>请选择</Option>}
          onChange={this.onChange}
        />
        <Select
          data={data}
          value={this.state.value}
          renderOption={item => <Option value={item.id}>{item.name}</Option>}
          defaultOption={<Option>请选择</Option>}
          onChange={this.onChange}
          disabled
        />
      </div>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

### Select largedata 数据

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

function onChange(value, item){
  console.log(value, item);
}

function largeData() {
  const result = [];
  for (let i = 0; i < 1000; i++) {
    result.push({
      key: `large-${i}`,
      name: `large-data-${i}`
    });
  }
  return result;
}

ReactDOM.render((
  <div>
    <Select
      data={largeData()}
      renderOption={item => <Option value={item.key}>{item.name}</Option>}
      defaultOption={<Option>请选择</Option>}
      onChange={onChange}
    />
    <p />
    <Select
      data={largeData()}
      renderOption={item => <Option value={item.key}>{item.name}</Option>}
      defaultOption={<Option>请选择</Option>}
      onChange={onChange}
      pretty
    /> (启用 pretty 效果)
  </div>
), _react_runner_);
```
---demoend

### Select 可搜索的

搜索匹配字段，，默认同时匹配 `Option` 的 `value` 和 `children`, 注意仅当 `children` 为基础数据类型时才能匹配成功。

优先匹配 `value` 属性。

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

function onChange(value, item){
  console.log(value, item);
}

ReactDOM.render(
  <Select searchable onChange={onChange}>
    <Option>请选择</Option>
    <Option value="0">张三</Option>
    <Option value="1">李四</Option>
    <Option value="2">王五</Option>
  </Select>, _react_runner_);
```
---demoend

### Select 自定义搜索匹配

> 当存在自定义的 children 时，如果不设置 `searchLabel`，默认的搜索匹配将无法执行。

---demo
```js
import { Select, Icon } from 'amos-framework';

const Option = Select.Option;

function onChange(value, item){
  console.log(value, item);
}

function customSearch(searchValue, optionProps){
  const { value, children, searchLabel } = optionProps;

  // 仅匹配 children
  if (typeof children === 'object'){
    return searchLabel?.indexOf(searchValue) !== -1;
  }
  return children.indexOf(searchValue) !== -1;

  // 支持匹配 children 和 value，非简单类型的 children 不适合
  // return children.indexOf(searchValue) !== -1 || String(value).indexOf(searchValue) !== -1;
}

ReactDOM.render(
  <Select searchable onChange={onChange} customSearch={customSearch}>
    <Option>请选择</Option>
    <Option value="0">张三</Option>
    <Option value="1">李四</Option>
    <Option value="2">王五</Option>
    <Option value="3">赵六</Option>
    <Option value="4">孙七</Option>
    <Option value="5">周八</Option>
    <Option value="6">吴九</Option>
    <Option value="7" searchLabel="郑十"><font color="red">郑十</font></Option>
    <Option value="8" searchLabel="江流儿朱大膘"><Icon icon="frown" />江流儿朱大膘</Option>
  </Select>, _react_runner_);
```
---demoend


### Select 启用搜索外部更新状态

> 启用搜索时，外部更新了值，不会改变已经输入的搜索条件

---demo
```js
import { Select, Input } from 'amos-framework';

const Option = Select.Option;

const data = [
  { id: '0', name: '张三' },
  { id: '1', name: '李四' },
  { id: '2', name: '王五' }
];

class Demo extends Component {
  state = {
  	value: '1'
  };

  onChange = (value, item) => {
    console.log(value, item);
    this.setState({
      value
    });
  }

  iptChange = (e) => {
  	this.setState({
    	value: e.target.value
    });
  }

	render(){
    return (
      <div>
        <Input value={this.state.value} onChange={this.iptChange} />
        <Select
          data={data}
          value={this.state.value}
          searchable
          renderOption={item => <Option value={item.id}>{item.name}</Option>}
          defaultOption={<Option>请选择</Option>}
          onChange={this.onChange}
        />
      </div>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

### Select 启用清除

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

function onChange(value, item){
  console.log(value, item);
}

ReactDOM.render(
  <Select onChange={onChange} hasClear defaultValue="1">
    <Option>请选择</Option>
    <Option value="0">张三</Option>
    <Option value="1">李四</Option>
    <Option value="2">王五</Option>
  </Select>, _react_runner_);
```
---demoend

### Select 禁用

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

ReactDOM.render(
  <Select disabled>
    <Option>请选择</Option>
    <Option value="0">张三</Option>
    <Option value="1">李四</Option>
    <Option value="2">王五</Option>
  </Select>, _react_runner_);
```
---demoend

### Select 设置内部弹出层

> 设置内部弹出层，此时可以设置 closeOnScroll 为 `false`，提高交互性

---demo
```js
import { Select } from 'amos-framework';

const Option = Select.Option;

function onChange(value, item){
  console.log(value, item);
}

ReactDOM.render(
  <Select onChange={onChange} defaultValue="1" popInset closeOnScroll={false}>
    <Option>请选择</Option>
    <Option value="0">张三</Option>
    <Option value="1">李四</Option>
    <Option value="2">王五</Option>
  </Select>, _react_runner_);
```
---demoend

## props

### Select props

| params | type | default | description |
|--------- |-------- |--------- |-------- |
| prefixCls | string | `amos-select` | 组件默认clssname prefix |
| className | string | - | 组件自定义 clssname |
| value | Number or string | - | 选中的值 |
| defaultValue | Number or string | - | 初始化时选中的值（不可控） |
| onChange | func | - | 切换选择后的回调，`function(value, item)`，当前数据项或 option.props |
| data | array | - | 数据源，结合 renderOption 属性定义 Option 渲染逻辑 |
| renderOption | func | - | data 方式时 Option 渲染回调，参数为当前数据和索引，返回一个 Option |
| defaultOption | ReactElement | - | data 方式时默认的 Option，通常针对空值时的选项 |
| placeholder | string | 请选择 | 无 `value` 且无选项匹配时 Select 显示的内容，默认 `请选择` |
| searchable | bool | - | 是否可搜索，搜索范围默认为 `Option` 的 `value` 和 `children`。优先匹配 `children` |
| customSearch | `func: (searchValue, optionProps) => boolean` | - | 自定义搜索，将会覆盖默认的搜索匹配 |
| searchPlaceholder | string | 请输入关键词搜索 | 搜索框 placeholder，默认`请输入关键词搜索` |
| disabled | bool | - | 是否禁用 |
| size | string | - | 尺寸,除了默认值外，可选值 `sm`、`lg` |
| minWidth | number | 160 | 最小宽度，默认 160 |
| noOptionsContent | string | 无选项 | 无 option 时显示的内容，默认`无选项` |
| noMatchingContent | string | 无匹配选项 | 有 `value` 但无选项匹配时显示的内容，默认`无匹配选项` |
| closeOnScroll | bool | true | 外围进行滚动时，关闭dropdown，需要`document`能触发 `scroll` 事件 |
| scrolling | bool | - | 正在进行滚动，针对无法自动触发`scroll`时，可手动进行设置`scrolling`, 以强制关闭dropdown |
| pretty | bool | - | 启用内置美化，主要用于内部组件统一效果，如：滚动条 |
| popoverClassName | String | - | popover 弹出层自定义 className |
| popoverStyle | Object | - | 弹出层自定义样式 |
| popoverContentStyle | Object | - | 弹出层内容自定义样式 |
| searchStyle | Object | - | 启用 search 时，search 组件的样式, 数据格式为：`{ style: {}, inputStyle: {}, btnStyle: {} }` |
| hasClear | bool | - | 是否可清除，since v1.8.6 添加 |
| popInset | bool | - | 弹出层是否内置，默认在 body 上，since v1.8.12 添加 |
| htmlTitle | String | - | select 外层 div 的 html title 内容 |

> 注意，如果发现下拉菜单跟随页面滚动，设置 `closeOnScroll` 为 `true`，如果还不起效，请检查 `document` 是否无法触发 `scroll` 事件。如果是自定义的 `scrollbar`，无法触发 `document`时，当scroll start时可手动设置 `scrolling=true`, scroll stop时，设置 `scrolling=false`。同时防止主文件body节点设置 `overflow：auto/scroll/hidden`，以防造成scroll滑动监听整体失效。
>
> 启用 hasClear 时，也会调用 `onChange` 传参为 `('', null)`，此时 value 将会赋值为空字符串，因此，禁止采用空字符串声明 option 中的 value。


### Select.Option props

| params | type | default | description |
|--------- |-------- |--------- |-------- |
| prefixCls | string | `amos-select` | 组件默认clssname prefix |
| className | string | - | 组件自定义 clssname |
| value | Number or string | - | 值，与 `Select value` 对应，数据类型也要一致 |
| selected | bool | - | 选中 |
| active | bool | - | 当前 |
| children | ReactNode | - | option 显示内容，如果是非基本数据类型，则需要设置 `searchLabel` 用于搜索匹配 |
| searchLabel | Sring | - | 自定义搜索匹配，仅当 children 不是基本数据类型数据时，才起效 |

> 当外部控制 `value` 时，`undefined` 值和空字符串需要自行处理。此时空字符串无法匹配到 `undefined` 类型的 `defaultOption`

```js
<Select searchable onChange={onChange} customSearch={customSearch}>
  <Option>请选择</Option>
  <Option value="0">张三</Option>
  {/* 需要提供 searchLabel */}
  <Option value="8" searchLabel="江流儿朱大膘"><Icon icon="frown" />江流儿朱大膘</Option>
</Select>
```

## Methods

* closeDropdown()

## 注意事项

由于 Select 的弹出层，默认是 body 下，因此，当 Select 滚动区域处于 body 时，则不会出现滚动时，弹出层偏移问题。

如果 Select 处于其它滚动区域时，此时会出现滚动时，弹出层不一起滚动的情景，该方式有两种解决方法：

方法一：设置 `popInset=true`

方法二: 设置 `popoverProps={{ getPopContainer }}`，将节点设置在触发节点之下，具体实现如下：

```js
function getPopContainer(){
  const tar = document.createElement('div');
  // 获取滚动区域
  const _parent = document.getElementById('mySelect') || document.body;
  _parent.appendChild(tar);

  // 注意，此处不能直接 返回 _parent，因为 Popover 处理逻辑中，组件卸载时会删除 `getPopContainer()` 方法返回的节点
  return tar;
}
```

为什么不能设置在滚动区域节点之下？

> 组件底层获取滚动 top 和 left 值，采用的是 `body.scrollLeft or body.scrollTop` 方法，同时获取 trigger 节点的偏移值，采用的是 `triggerNode.getBoundingClientRect()`
> 均是采用的 视窗 相关值，因此不能采用滚动区域。
