Select  选择器

Guide#

何时使用#

Select#

如果你不期望用户输入的值生效而仅仅是选择,那么使用 Select. 同时可以使用 Select 的 showSearch 属性进行过滤。

AutoComplete#

AutoComplete 会保留用户输入的值,本质上是 Input 组件,扩展了 autocomplete 的能力,所以 Input 组件的属性可以直接透传。

常见问题#

出现类似的flatternChildren的warning#

Select 默认使用 value 作为菜单项的 key,如果没有设置 key 值,则使用默认的序列 index 作为 key 值,确保这些值不会发生重复。

dataSource的使用#

Select 同时支持 children 和在 props 中传入 dataSource 作为数据源, 如果同时设置, 则以 children 为准.

注意:1. Select 默认使用 value 作为渲染的菜单项的 key 值,所以 value 不能重复, 否则无法渲染下拉菜单。2. value 不允许出现 null/undefined/object/array 类型数值

  1. children的方式
<Select>
    <Select.Option value="option1">option1</Select.Option>
    <Select.Option value="option2">option2</Select.Option>
    <Select.Option disabled>disabled</Select.Option>
</Select>;
  1. props的方式
const dataSource = [
    {label:'option1', value:'option1'},
    {label:'option2', value:'option2'},
    {label:'disabled', disabled:true}
];

<Select dataSource={dataSource}/>

定制弹出层#

参见示例中的 弹层定制。唯一需要注意的是 overlay 的元素记得透传 props. 这是因为 Overlay 的弹层的动画是依靠 className 实现的,如果不透传 props 则会造成无法监听到动画播放结束的事件。

API#

import { Select } from '@alifd/next';

const Option = Select.Option;

const onChange = function (value) {
    console.log(value);
};
const onBlur = function (e) {
    console.log(/onblur/,e);
};

const onToggleHighlightItem = function (item, type) {
    console.log(item, type);
};

ReactDOM.render(<Select id="basic-demo" onChange={onChange} onBlur={onBlur} onToggleHighlightItem={onToggleHighlightItem} defaultValue="jack" aria-label="name is" showSearch hasClear>
    <Option value="jack">Jack</Option>
    <Option value="frank">Frank</Option>
    <Option value="hugo">Hugo</Option>
</Select>, mountNode);

简单

code collapse
import { Select } from '@alifd/next';

const dataSource = [
    {value: '10001', label: 'Lucy King'},
    {value: 10002, label: 'Lily King'},
    {value: 10003, label: 'Tom Cat', disabled: true},
    {label: 'Special Group', children: [
        {value: -1, label: 'FALSE'},
        {value: 0, label: 'ZERO'}
    ]}
];

function handleChange(value) {
    console.log(value);
}

ReactDOM.render(<Select aria-label="tag mode" mode="tag" defaultValue={['10001']} onChange={handleChange} dataSource={dataSource} style={{width: 300}} />, mountNode);

标签模式,输入的内容可以作为选项

code collapse
import { Select } from '@alifd/next';

const dataSource = [
    {value: '10001', label: 'Lucy King'},
    {value: 10002, label: 'Lily King'},
    {value: 10003, label: 'Tom Cat', disabled: true},
    {label: 'Special Group', children: [
        {value: -1, label: 'FALSE'},
        {value: 0, label: 'ZERO'}
    ]}
];

function handleChange(value) {
    console.log(value);
}

ReactDOM.render(
    <div>
        <Select mode="multiple" showSearch defaultValue={['10001']} onChange={handleChange} dataSource={dataSource} style={{width: 300}} />
    </div>
, mountNode);

多选模式, 通过 showSearch 可以开启搜索, 但搜索值不可用作选项

code collapse
import { Select, Balloon } from '@alifd/next';

const { Tooltip } = Balloon;

const dataSource = [
    {value: '10001', label: 'Lucy King'},
    {value: 10002, label: 'Lily King'},
    {value: 10003, label: 'Tom Cat', disabled: true},
    {label: 'Special Group', children: [
        {value: -1, label: 'FALSE'},
        {value: 0, label: 'ZERO'}
    ]}
];

function handleChange(value) {
    console.log(value);
}

const maxTagPlaceholder = (selectedValues, totalValues) => {
    const trigger = <span>{`${selectedValues.length}/${totalValues.length}`}</span>;
    const labels = selectedValues.map(obj => obj.label);

    return <Tooltip trigger={trigger}>{ labels.join(', ') }</Tooltip>;
};

ReactDOM.render(
    <div>
        hasSelectAll:<br/>
        <Select hasSelectAll mode="multiple" onChange={handleChange} dataSource={dataSource} style={{width: 200}} />
        <br /><br />
        maxTagCount=2<br />
        <Select maxTagCount={2} defaultValue={['10001', '10002', '-1']} mode="multiple" onChange={handleChange} dataSource={dataSource} style={{width: 200}} /> <br /><br />
        maxTagPlaceholder<br />
        <Select maxTagCount={2} maxTagPlaceholder={maxTagPlaceholder} defaultValue={['10001', '10002', '-1']} mode="multiple" onChange={handleChange} dataSource={dataSource} style={{width: 200}} /><br /><br />
        tagInline <br />
        <Select maxTagCount={2} tagInline mode="multiple" defaultValue={['10001', '10002', '-1']}  onChange={handleChange} dataSource={dataSource} style={{width: 200}} /><br /><br />
        maxTagPlaceholder<br />
        <Select maxTagCount={2} tagInline maxTagPlaceholder={maxTagPlaceholder} defaultValue={['10001', '10002', '-1']}  mode="multiple" onChange={handleChange} dataSource={dataSource} style={{width: 200}} /><br /><br />
    </div>
, mountNode);

多选模式

code collapse
import { Select } from '@alifd/next';

const dataSource = [
    {value: '10001', label: 'Lucy King'},
    {value: 10002, label: 'Lily King'},
    {value: 10003, label: 'Tom Cat', disabled: true},
    {label: 'Special Group', children: [
        {value: -1, label: 'FALSE'},
        {value: 0, label: 'ZERO'}
    ]}
];

const ctrlDataSources = {
    mode: ['single', 'multiple', 'tag'],
    size: ['small', 'medium', 'large'],
    showSearch: [true, false],
    hasArrow: [true, false],
    hasBorder: [true, false],
    hasClear: [true, false]
};

class Demo extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            value: null,
            size: undefined,
            mode: undefined,
            hasArrow: undefined,
            hasBorder: undefined,
            showSearch: undefined,
            hasClear: undefined
        };

        this.handleChange = this.handleChange.bind(this);
        this.handleCtrlChange = this.handleCtrlChange.bind(this);
    }

    handleCtrlChange(key, value) {
        this.setState({[key]: value});

        if (key === 'mode') {
            this.setState({value: null});
        }
    }

    handleChange(value, item) {
        console.log('handleChange: value: ', value, item);
        this.setState({value});
    }

    renderCtrlNodes(state) {
        const ctrlNodes = [];
        let k;
        for (k in ctrlDataSources) {
            if (ctrlDataSources.hasOwnProperty(k)) {
                ctrlNodes.push(
                    <Select key={k}
                        label={`${k}: `}
                        value={state[k]}
                        id={k}
                        dataSource={ctrlDataSources[k]}
                        onChange={this.handleCtrlChange.bind(this, k)} />
                );
            }
        }

        return ctrlNodes;
    }

    render() {

        return (
            <div className="demo-container">
                <div className="demo-controller">{this.renderCtrlNodes(this.state)}</div>
                <Select
                    {...this.state}
                    onChange={this.handleChange}
                    dataSource={dataSource} />
            </div>
        );
    }
}

ReactDOM.render(<Demo />, mountNode);
.demo-container {
    padding: 16px;
    background-color: #F8F8F8;
}

.demo-controller {
    padding: 12px 12px 4px;
    margin-bottom: 16px;
    border: 2px dashed #ddd;
}

.next-select {
    margin-right: 8px;
    margin-bottom: 8px;
}

演示了 Select 的多种形态.

code collapse
import { Select } from '@alifd/next';

const provinceData = ['Zhejiang', 'Hubei', 'Jiangsu'];
const cityData = {
    Zhejiang: ['Hangzhou', 'Ningbo', 'Wenzhou'],
    Hubei: ['Wuhan', 'Yichang', 'Jingzhou'],
    Jiangsu: ['Nanjing', 'Suzhou', 'Zhenjiang']
};

class Demo extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            data: [],
            disabled: true
        };

        this.handleProvinceChange = this.handleProvinceChange.bind(this);
        this.handleCityChange = this.handleCityChange.bind(this);
    }

    handleProvinceChange(value) {
        const data = cityData[value];
        this.setState({data, province: value, city: '', disabled: !data});
    }

    handleCityChange(value) {
        this.setState({city: value});
        console.log(this.state.province, value);
    }

    render() {
        const {data, disabled, province, city} = this.state;

        return (
            <div className="demo-container">
                <Select placeholder="Select Province" dataSource={provinceData} value={province} onChange={this.handleProvinceChange} />
                <Select placeholder="Select City" dataSource={data} value={city} onChange={this.handleCityChange} disabled={disabled}/>
            </div>
        );
    }
}

ReactDOM.render(<Demo/>, mountNode);
.next-select {
    margin-right:10px;
}

.demo-container {
    background-color: #F8F8F8;
    padding: 16px;
}

使用 Select 构建级联选择框

code collapse
import { Select } from '@alifd/next';

const {Option, OptionGroup} = Select;

const dataSource = [{
    label: 'label1',
    children: [{
        label: 'label1-1',
        value: 'text1-1'
    }]
}, {
    label: 'label2',
    children: [{
        label: 'label2-1',
        value: 'text2-1'
    }]
}];

ReactDOM.render(
    <div className="demo-container">
        <Select placeholder="OptionGroup">
            <OptionGroup label="group1">
                <Option value="small">Small</Option>
                <Option value="medium">Medium</Option>
                <Option value="large">Large</Option>
            </OptionGroup>
            <OptionGroup label="group2">
                <Option value="small2">Small2</Option>
                <Option value="medium2">Medium2</Option>
                <Option value="large2">Large2</Option>
            </OptionGroup>
        </Select>
        <Select placeholder="optgroup">
            <option value="apple">Apple</option>
            <option value="orange">Orange</option>
            <option value="banana">Banana</option>
            <optgroup label="Pets Group">
                <option value="cat">Cat</option>
                <option value="rabbit">Rabbit</option>
                <option value="dog" disabled>Dog</option>
            </optgroup>
        </Select>
        <Select placeholder="item.children" dataSource={dataSource}/>
    </div>,
    mountNode
);
.next-select {
    margin-right:10px;
}

.demo-container {
    background-color: #F8F8F8;
    padding: 16px;
}

使用 OptionGroup 针对选项进行分组,也可以使用原生的 html 标签 optgroup

code collapse
import { Select } from '@alifd/next';

const dataSource = [
    {value: 'Lilith', age: 22, gender: 'F'},
    {value: 'Tom Cat', age: 28, gender: 'M'},
    {value: 'Jim Green', age: 18, gender: 'M'},
    {value: 'Monkey King', age: 999, gender: 'M'}
];

const handleChange = value => {
    console.log('handleChange: ', value);
};

const valueRender = v => {
    return `${v.value} / ${v.gender} / ${v.age}`;
};

ReactDOM.render(<Select mode="multiple" placeholder="custom value" valueRender={valueRender} dataSource={dataSource} onChange={handleChange} />, mountNode);

Select 的 value 可以是任意非空类型的值,但是要保证 toString() 后是唯一的。

code collapse
import { Select, Divider, Icon, Button } from '@alifd/next';

const Option = Select.Option;

const dataSource = [
    {value: '10001', label: 'Lucy King'},
    {value: 10002, label: 'Lily King'},
    {value: 10003, label: 'Tom Cat', disabled: true},
];

const generateData = (base, total) => {
    const arr = [];
    for(let i = 0; i< total; i++) {
        arr.push(`extra-${base + i}`);
    }
    return arr;
};

class App extends React.Component {
    state = {
        dataSource,
    };
    loadMore = () => {
        const ds = this.state.dataSource;
        this.setState({
            dataSource: [ ...ds, ...generateData(ds.length, 5)]
        });
    }
    render() {
        const menuProps = {
            header: <div style={{padding: '0 4px', textAlign:'center'}}>
                <div>Header</div>
                <Divider style={{marginTop:0, marginBottom: 4}}/>
            </div>,
            footer: <div style={{padding: '0 4px', textAlign:'center'}}>
                <Divider style={{marginBottom:0, marginTop: 4}}/>
                <Button text type="primary" onClick={this.loadMore}>Load More...</Button>
            </div>
        };
        return <Select hasSelectAll mode="multiple"  dataSource={this.state.dataSource} style={{width: 200}} menuProps={menuProps}/ >;
    }
};

ReactDOM.render(<App />, mountNode);

通过 MenuProps 自定义 Select 弹窗的头部和底部(注意 MenuProps.header 不能与 hasSelectAll 同时出现, MenuProps.header 优先级更高)

code collapse
import { Select } from '@alifd/next';

const dataSource = [
    {label: '1', value: 1},
    {label: '10', value: 10},
    {label: '50', value: 50},
    {label: '100', value: 100}
];

const handleChange = value => {
    console.log('handleChange: ', value);
};

ReactDOM.render(<Select label="size:" innerAfter={<span style={{color: '#999', marginRight: 4}}>GB</span>} dataSource={dataSource} onChange={handleChange} />, mountNode);

Select 增加前后缀

code collapse
import { Select } from '@alifd/next';
import jsonp from 'jsonp';

let timestamp = Date.now();

class Demo extends React.Component {
    state = {
        dataSource: []
    }

    handleSearch = (value) => {
        if (this.searchTimeout) {
            clearTimeout(this.searchTimeout);
        }
        this.searchTimeout = setTimeout(() => {
            // eslint-disable-next-line handle-callback-err
            value ? jsonp(`https://suggest.taobao.com/sug?code=utf-8&q=${value}`, (err, data) => {
                const dataSource = data.result.map(item => ({
                    label: item[0], value: (timestamp++).toString(36)
                }));
                this.setState({dataSource});
            }) : this.setState({dataSource: []});
        }, 100);
    }

    render() {
        return (
            <div className="demo-container">
                <Select showSearch placeholder="select search" filterLocal={false} dataSource={this.state.dataSource} onSearch={this.handleSearch} style={{width: 200}}/>
            </div>
        );
    }
}

ReactDOM.render(<Demo/>, mountNode);

使用 showSearch 显示搜索框,如果需要动态更新 dataSource,需要关闭 filterLocal

code collapse
import { Select } from '@alifd/next';

const dataSource = [
    {value: '10001', label: 'Lucy King'},
    {value: 10002, label: 'Lily King'},
    {value: 10003, label: 'Tom Cat', disabled: true},
    {label: 'Special Group', children: [
        {value: new Date(), label: 'new Date()'},
        {value: false, label: 'FALSE'},
        {value: 0, label: 'ZERO'}
    ]}
];

function handleChange(value) {
    console.log(value);
}

ReactDOM.render(<Select useDetailValue defaultValue={{value: '10001', label: 'Lucy King'}} onChange={handleChange} dataSource={dataSource} style={{width: 150}} />, mountNode);

useDetailValuevalue 从字符串变成对象

code collapse
import { Select } from '@alifd/next';

const dataSource = [
    'Lucy King',
    'Lily King',
    'Jim Green',
    {
        label: 'Chinese',
        children: [
            {value: 'Hang Meimei', label: 'Hang Meimei'},
            'Li Lei',
            {value: 'Gao Hui', label: 'Gao Hui', disabled: true},
            'Zhang San',
            'Li Si',
            'Wang Wu',
            {value: 'Zhao Benshan', label: 'Zhao Benshan', disabled: true},
            'Sun Yang',
            'Song Shuying'
        ]
    },
    {
        label: 'Pets',
        children: [
            'Poly',
            'Kitty'
        ]
    }
];

const onChange = (v) => {
    console.log(v);
};

ReactDOM.render(<Select.AutoComplete style={{width: 300}} onChange={onChange} dataSource={dataSource} />, mountNode);

AutoComplete 继承了 Input 的能力,并在其基础上增加了 autoComplete 的功能。 对于设置为AutoComplete为off不生效对的情况,可以参考 MDN 中进行设置。

code collapse
import { Select } from '@alifd/next';

const {AutoComplete} = Select;
const dataSource = [
    'Lucy King',
    'Lily King',
    'Jim Green',
    {
        label: 'Chinese',
        children: [
            {value: 'Hang Meimei', label: 'Hang Meimei'},
            'Li Lei',
            {value: 'Gao Hui', label: 'Gao Hui', disabled: true},
            'Zhang San',
            'Li Si',
            'Wang Wu',
            {value: 'Zhao Benshan', label: 'Zhao Benshan', disabled: true},
            'Sun Yang',
            'Song Shuying'
        ]
    },
    {
        label: 'Pets',
        children: [
            'Poly',
            'Kitty'
        ]
    }
];

const ctrlDataSources = {
    size: ['small', 'medium', 'large'],
    disabled: [true, false],
    hasClear: [true, false]
};

class Demo extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            value: null,
            size: undefined,
            disabled: undefined,
            hasClear: undefined
        };

        this.handleChange = this.handleChange.bind(this);
        this.handleCtrlChange = this.handleCtrlChange.bind(this);
    }

    handleCtrlChange(key, value) {
        this.setState({[key]: value});

        if (key === 'mode') {
            this.setState({value: null});
        }
    }

    handleChange(value) {
        console.log('handleChange: value: ', value);
        this.setState({value});
    }

    renderCtrlNodes(state) {
        const ctrlNodes = [];
        let k;
        for (k in ctrlDataSources) {
            if (ctrlDataSources.hasOwnProperty(k)) {
                ctrlNodes.push(
                    <Select key={k}
                        label={`${k}: `}
                        value={state[k]}
                        dataSource={ctrlDataSources[k]}
                        onChange={this.handleCtrlChange.bind(this, k)} />
                );
            }
        }

        return ctrlNodes;
    }

    render() {

        return (
            <div className="demo-container">
                <div className="demo-controller">{this.renderCtrlNodes(this.state)}</div>
                <AutoComplete
                    {...this.state}
                    style={{maxWidth: 300}}
                    onChange={this.handleChange}
                    dataSource={dataSource} />
            </div>
        );
    }
}

ReactDOM.render(<Demo />, mountNode);
.demo-container {
    padding: 16px;
    background-color: #F8F8F8;
}

.demo-controller {
    padding: 12px 12px 4px;
    margin-bottom: 16px;
    border: 2px dashed #ddd;
}

.next-select {
    margin-right: 8px;
    margin-bottom: 8px;
}

AutoComplete 大小、disabled、清除

code collapse
import { Select, Radio } from '@alifd/next';

const {AutoComplete} = Select;
const dataSource = [
    {value: 'Hang Meimei', label: '韩梅梅'},
    {value: 'Gao Hui', label: '高辉'},
    {value: 'Zhang San', label: '张三'},
    {value: 'Li Si', label: '李四'},
    {value: 'Wang Wu', label: '王五'},
    {value: 'Sun Yang', label: '孙杨'},
];

class Demo extends React.Component {
    state = {
        value: '',
        fillProps: 'value',
    };
    handleCtrlChange = (fillProps) => {
        this.setState({fillProps});
    }

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

    render() {
        return (
            <div >
                <div style={{marginBottom: 10}}>
                    fillProps:  
                    <Radio.Group
                        style={{marginLeft: 8}}
                        shape="button"
                        value={this.state.fillProps}
                        onChange={this.handleCtrlChange}
                    >
                        <Radio value="value">value</Radio>
                        <Radio value="label">label</Radio>
                    </Radio.Group>
                </div>
                <AutoComplete
                    value={this.state.value}
                    fillProps={this.state.fillProps}
                    style={{width: 300}}
                    onChange={this.handleChange}
                    dataSource={dataSource} />
            </div>
        );
    }
}

ReactDOM.render(<Demo />, mountNode);

AutoComplete 是一个带填充功能的 Input, 通过 fillProps 选择填充 {label,value} 的哪个字段到 Input

code collapse
import { Select } from '@alifd/next';
import jsonp from 'jsonp';

const {AutoComplete} = Select;

class Demo extends React.Component {
    state = {
        dataSource: []
    };

    handleChange = value => {
        clearTimeout(this.searchTimeout);
        this.searchTimeout = setTimeout(() => {
            // eslint-disable-next-line handle-callback-err
            jsonp(`https://suggest.taobao.com/sug?code=utf-8&q=${value}`, (err, data) => {
                const dataSource = data.result.map(item => item[0]);
                this.setState({dataSource});
            });
        }, 100);
    }

    render() {
        return (
            <div className="demo-container">
                <AutoComplete
                    filterLocal={false}
                    placeholder="search from taobao"
                    onChange={this.handleChange}
                    dataSource={this.state.dataSource}/>
            </div>
        );
    }
}

ReactDOM.render(<Demo/>, mountNode);
.demo-container {
    background-color: #F8F8F8;
    padding: 16px;
}

使用动态数据填充 AutoComplete, 设置 filterLocal 为 false

code collapse
import { Select, Icon } from '@alifd/next';
import jsonp from 'jsonp';

const {AutoComplete} = Select;

class Demo extends React.Component {
    state = {
        dataSource: []
    }

    handleChange = (value) => {
        if (this.searchTimeout) {
            clearTimeout(this.searchTimeout);
        }
        this.searchTimeout = setTimeout(() => {
            // eslint-disable-next-line handle-callback-err
            jsonp(`https://suggest.taobao.com/sug?code=utf-8&q=${value}`, (err, data) => {
                const dataSource = data.result.map(item => {
                    return {
                        label: <div><Icon type="picture" size="small"/>&nbsp;{item[0]}</div>,
                        value: item[1],
                        originLabel: item[0]
                    };
                });
                this.setState({dataSource});
            });
        }, 100);
    }

    render() {
        return (
            <div className="demo-container">
                <AutoComplete onChange={this.handleChange}
                    filterLocal={false}
                    fillProps="originLabel"
                    placeholder="search from taobao"
                    dataSource={this.state.dataSource}/></div>
        );
    }
}
ReactDOM.render(<Demo/>, mountNode);
.next-select {
    margin-right:10px;
    vertical-align: middle;
}

.demo-container {
  background-color: #F8F8F8;
  padding: 16px;
}

.demo-container p {
    margin-top:0;
}

展示较为复杂的内容展示

code collapse
import { Select, Icon } from '@alifd/next';

const dataSource = [
    {value: '#FF0000', label: 'red', title: 'red'},
    {value: '#00AA00', label: 'green', title: 'green'},
    {value: '#B482DB', label: 'purple', title: 'purple'},
    {value: '#F17334', label: 'orange', title: 'orange'},
    {value: '#66CCFF', label: 'blue', title: 'blue'}
];

const itemRender = item => {
    return (
        <span>
            <Icon type="account" size="xs" style={{color: item.value}} />
            <Icon type="account" size="xs" style={{color: item.value}} />
            <Icon type="account" size="xs" style={{color: item.value}} />
            <Icon type="account" size="xs" style={{color: item.value}} />
            <Icon type="account" size="xs" style={{color: item.value}} />
        </span>
    );
};

const valueRender = item => {
    return <span><Icon type="account" size="xs" style={{color: item.value}} /> {item.label}</span>;
};

const dataSource2 = [
    'Lorem ipsum dolor sit amet',
    'consectetur adipisicing elit',
    'sed do eiusmod tempor incididunt',
    'ut labore et dolore magna aliqua.',
    'Ut enim ad minim veniam',
    'quis nostrud exercitation',
    'ullamco laboris nisi ut',
    'aliquip ex ea commodo consequat',
    'Duis aute irure dolor in',
    'reprehenderit in voluptate',
    'velit esse cillum dolore eu',
    'Fugiat nulla pariatur excepteur sint',
    'occaecat cupidatat non proident',
    'sunt in culpa qui officia',
    'deserunt mollit anim id est laborum'
];

// highlight keywords
const itemRender2 = (item, searchKey) => {
    let label = item.label;
    if (searchKey && searchKey.length) {
        const key = searchKey.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
        const reg = new RegExp(`(${key})`, 'ig');
        label = label.replace(reg, x => `<span class="next-select-highlight">${x}</span>`);
    }

    return <span dangerouslySetInnerHTML={{__html: label}} />;
};

ReactDOM.render(
    <div className="demo-container">
        <Select dataSource={dataSource} itemRender={itemRender} valueRender={valueRender} placeholder="pick your color" />
        <Select showSearch dataSource={dataSource2} itemRender={itemRender2} placeholder="highlight keywords" style={{minWidth: 200}} />
    </div>,
    mountNode
);
.demo-container {
    padding: 16px;
    background-color: #F8F8F8;
}

.demo-container .next-select {
    margin-right: 10px;
}

通过 itemRendervalueRender (仅 Select) 自定义渲染的节点内容。

code collapse
import { Select } from '@alifd/next';
import classNames from 'classnames';
/* eslint-disable react/prop-types, react/no-multi-comp */

// prevent onBlur
function preventDefault(e) {
    e.preventDefault();
}

class Menu extends React.Component {
    data = [{
        label: 'value1',
        value: 1
    }, {
        label: 'value2',
        value: 2
    }];

    onClick(item) {
        this.props.onChange(item);
    }

    renderItems() {
        return this.data.map(item => <li onClick={this.onClick.bind(this, item)} key={item.value}>{item.label}</li>);
    }

    render() {
        const {className, ...others} = this.props;
        const cls = classNames('overlay-content', className);

        return (
            <ul className={cls} {...others}>
                {this.renderItems()}
            </ul>
        );
    }
}

class Demo extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            value: null
        };
    }

    handleSelect = (value) => {
        this.setState({
            value,
            visible: false
        });
    }

    onVisibleChange = (visible) => {
        this.setState({
            visible
        });
    }

    render() {
        const popupContent = <Menu onChange={this.handleSelect} onMouseDown={preventDefault}/>;
        const popupProps = {
            triggerClickKeycode: [13, 32, 40] // space, enter, down-arrow
        };

        return (
            <div className="demo-container">
                <Select
                    placeholder="custom popupContent"
                    visible={this.state.visible}
                    onVisibleChange={this.onVisibleChange}
                    value={this.state.value}
                    popupProps={popupProps}
                    popupContent={popupContent} />
            </div>
        );
    }
}
ReactDOM.render(<Demo/>, mountNode);
.demo-container {
  background-color: #F8F8F8;
  padding: 16px;
}

.demo-container p {
    margin-top:0;
}

.overlay-content {
    border:1px solid #DDDDDD;
    padding:10px;
    background: #FFFFFF;
    margin:0;
    font-size: 12px;
    font-family: Arial;
    box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}

.overlay-content li {
    list-style: none;
    line-height:30px;
    padding: 0 5px;
    cursor: pointer;
}

.overlay-content li:hover {
    background: #F8F8F8;
}

.overlay-content li:last-child {
    border-width:0;
}

通过 popupContent 定制 Select 弹层, Select 使用 popupContent 中渲染出的 item 的 value 作为菜单项的key,如果没有提供或者自定义渲染 key 请使用 valueRender

code collapse
import { Select } from '@alifd/next';

const Option = Select.Option;

const onChange = function (value) {
    console.log(value);
};

function generateItem(index) {
    return {label: `option${index}`, value: `option${index}`};
}

function generateOption(index) {
    return <Option key={`option${index}`} value={`option${index}`}>{`option${index}`}</Option>;
}

function generateData(len, isOption) {
    const data = [];

    for (let i = 0; i < len; i++) {
        isOption ? data.push(generateOption(i)) : data.push(generateItem(i));
    }

    return data;
}

ReactDOM.render(
    <div>
        <Select dataSource={generateData(100)} useVirtual onChange={onChange} defaultValue="option0" />
        &nbsp;&nbsp;&nbsp;&nbsp;
        <Select useVirtual onChange={onChange} defaultValue="option50">
            {generateData(100, true)}
        </Select>
    </div>
    , mountNode);

select 配合无限滚动

code collapse
import { Select } from '@alifd/next';

const Option = Select.Option;

class App extends React.Component {
    constructor(props) {
        super(props);
        this.onChange = this.onChange.bind(this);
    }
    onChange(value) {
        console.log(value);
    };

    render() {
        return (<div>
            <span id="select-a11y">Select: </span>
            <Select onChange={this.onChange} defaultValue="jack" aria-labelledby="select-a11y">
                <Option value="jack">Jack</Option>
                <Option value="frank">Frank</Option>
                <Option value="hugo">Hugo</Option>
            </Select>
        </div>);
    }
}

ReactDOM.render(<App />, mountNode);

当聚焦在组件上时,通过aria-labelledby对组件进行描述。关于键盘操作请参考ARIA and KeyBoard

code collapse

# API

Select#

参数 说明 类型 默认值
size 选择器尺寸

可选值:
'small', 'medium', 'large'
Enum 'medium'
value 当前值,用于受控模式 any -
defaultValue 初始的默认值 any -
placeholder 没有值的时候的占位符 String -
autoWidth 下拉菜单是否与选择器对齐 Boolean true
label 自定义内联 label ReactNode -
hasClear 是否有清除按钮(单选模式有效) Boolean -
state 校验状态

可选值:
'error', 'loading'
Enum -
readOnly 是否只读,只读模式下可以展开弹层但不能选 Boolean -
disabled 是否禁用选择器 Boolean -
visible 当前弹层是否显示 Boolean -
defaultVisible 弹层初始化是否显示 Boolean -
onVisibleChange 弹层显示或隐藏时触发的回调

签名:
Function(visible: Boolean, type: String) => void
参数:
visible: {Boolean} 弹层是否显示
type: {String} 触发弹层显示或隐藏的来源 fromContent 表示由Dropdown内容触发; fromTrigger 表示由trigger的点击触发; docClick 表示由document的点击触发
Function func.noop
popupContainer 弹层挂载的容器节点 any -
popupClassName 弹层的 className any -
popupStyle 弹层的内联样式 Object -
popupProps 添加到弹层上的属性 Object {}
followTrigger 是否跟随滚动 Boolean -
popupContent 自定义弹层的内容 ReactNode -
menuProps 添加到菜单上的属性 Object -
filterLocal 是否使用本地过滤,在数据源为远程的时候需要关闭此项 Boolean true
filter 本地过滤方法,返回一个 Boolean 值确定是否保留

签名:
Function() => void
Function filter
onToggleHighlightItem 键盘上下键切换菜单高亮选项的回调

签名:
Function() => void
Function func.noop
useVirtual 是否开启虚拟滚动模式 Boolean -
dataSource 传入的数据源,可以动态渲染子项,详见 dataSource的使用 Array<Object/Boolean/Number/String> -
itemRender 渲染 MenuItem 内容的方法

签名:
Function(item: Object, searchValue: String) => ReactNode
参数:
item: {Object} 渲染节点的item
searchValue: {String} 搜索关键字(如果开启搜索)
返回值:
{ReactNode} item node
Function -
mode 选择器模式

可选值:
'single', 'multiple', 'tag'
Enum 'single'
notFoundContent 弹层内容为空的文案 ReactNode -
isPreview 是否为预览态 Boolean -
renderPreview 预览态模式下渲染的内容

签名:
Function(value: number) => void
参数:
value: {number} 评分值
Function -
onChange Select发生改变时触发的回调

签名:
Function(value: mixed, actionType: String, item: mixed) => void
参数:
value: {mixed} 选中的值
actionType: {String} 触发的方式, 'itemClick', 'enter', 'tag'
item: {mixed} 选中的值的对象数据 (useDetailValue=false有效)
Function -
hasBorder 是否有边框 Boolean -
hasArrow 是否有下拉箭头 Boolean true
showSearch 展开后是否能搜索(tag 模式下固定为true) Boolean false
onSearch 当搜索框值变化时回调

签名:
Function(value: String) => void
参数:
value: {String} 数据
Function func.noop
onSearchClear 当搜索框值被清空时候的回调

签名:
Function(actionType: String) => void
参数:
actionType: {String} 触发的方式, 'select'(选择清空), 'popupClose'(弹窗关闭清空)
Function func.noop
hasSelectAll 多选模式下是否有全选功能 Boolean/String -
fillProps 填充到选择框里的值的 key String -
useDetailValue onChange 返回的 value 使用 dataSource 的对象 Boolean -
cacheValue dataSource 变化的时是否保留已选的内容 Boolean true
valueRender 渲染 Select 展现内容的方法

签名:
Function(item: Object) => ReactNode
参数:
item: {Object} 渲染节点的item
返回值:
{ReactNode} 展现内容
Function item => item.label \ \ item.value
searchValue 受控搜索值,一般不需要设置 String -
tagInline 是否一行显示,仅在 mode 为 multiple 的时候生效 Boolean false
maxTagCount 最多显示多少个 tag Number -
maxTagPlaceholder 隐藏多余 tag 时显示的内容,在 maxTagCount 生效时起作用

签名:
Function(selectedValues: number, totalValues: number) => void
参数:
selectedValues: {number} 当前已选中的元素
totalValues: {number} 总待选元素
Function -
hiddenSelected 选择后是否立即隐藏菜单 (mode=multiple/tag 模式生效) Boolean -
onRemove tag 删除回调

签名:
Function(item: object) => void
参数:
item: {object} 渲染节点的item
Function func.noop
onFocus 焦点事件

签名:
Function() => void
Function func.noop
onBlur 失去焦点事件

签名:
Function() => void
Function func.noop

Select.AutoComplete#

参数 说明 类型 默认值
size 选择器尺寸

可选值:
'small', 'medium', 'large'
Enum 'medium'
value 当前值,用于受控模式 String/Number -
defaultValue 初始化的默认值 String/Number -
placeholder 没有值的时候的占位符 String -
autoWidth 下拉菜单是否与选择器对齐 Boolean true
label 自定义内联 label ReactNode -
hasClear 是否有清除按钮(单选模式有效) Boolean -
state 校验状态

可选值:
'error', 'loading'
Enum -
readOnly 是否只读,只读模式下可以展开弹层但不能选 Boolean -
disabled 是否禁用选择器 Boolean -
visible 当前弹层是否显示 Boolean -
defaultVisible 弹层初始化是否显示 Boolean -
onVisibleChange 弹层显示或隐藏时触发的回调

签名:
Function(visible: Boolean, type: String) => void
参数:
visible: {Boolean} 弹层是否显示
type: {String} 触发弹层显示或隐藏的来源 fromContent 表示由Dropdown内容触发; fromTrigger 表示由trigger的点击触发; docClick 表示由document的点击触发
Function func.noop
popupContainer 弹层挂载的容器节点 any -
popupClassName 弹层的 className any -
popupStyle 弹层的内联样式 Object -
popupProps 添加到弹层上的属性 Object {}
followTrigger 是否跟随滚动 Boolean -
popupContent 自定义弹层的内容 ReactNode -
menuProps 添加到菜单上的属性 Object -
filterLocal 是否使用本地过滤,在数据源为远程的时候需要关闭此项 Boolean true
filter 本地过滤方法,返回一个 Boolean 值确定是否保留

签名:
Function() => void
Function filter
onToggleHighlightItem 键盘上下键切换菜单高亮选项的回调

签名:
Function() => void
Function func.noop
useVirtual 是否开启虚拟滚动模式 Boolean -
dataSource 传入的数据源,可以动态渲染子项 Array<Object/String> -
itemRender 渲染 MenuItem 内容的方法

签名:
Function(item: Object) => ReactNode
参数:
item: {Object} 渲染节点的 item
返回值:
{ReactNode} item node
Function -
isPreview 是否为预览态 Boolean -
renderPreview 预览态模式下渲染的内容

签名:
Function(value: number) => void
参数:
value: {number} 评分值
Function -
onChange Select发生改变时触发的回调

签名:
Function(value: mixed, actionType: String, item: mixed) => void
参数:
value: {mixed} 选中的值
actionType: {String} 触发的方式, 'itemClick', 'enter', 'change'
item: {mixed} 选中的值的对象数据
Function -
fillProps 填充到选择框里的值的 key,默认是 value String 'value'

Select.OptionGroup#

参数 说明 类型 默认值
label 设置分组的文案 ReactNode -

Select.Option#

参数 说明 类型 默认值
value 选项值 any -
disabled 是否禁用 Boolean -

ARIA and KeyBoard#

按键 说明
Up Arrow 获取当前项前一项焦点
Down Arrow 获取当前项后一项焦点
Enter 打开列表或选择当前项
Esc 关闭列表