Select 选择器
如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。
安装方法
- 在命令行中执行以下命令
npm install @alifd/next@latest -S
Guide
何时使用
Select
如果你不期望用户输入的值生效而仅仅是选择,那么使用 Select. 同时可以使用 Select 的 showSearch 属性进行过滤。
AutoComplete
AutoComplete 会保留用户输入的值,本质上是 Input 组件,扩展了 autocomplete 的能力,所以 Input 组件的属性可以直接透传。
常见问题
出现类似的
Select 默认使用 value
作为菜单项的 key,如果没有设置 key 值,则使用默认的序列 index 作为 key 值,确保这些值不会发生重复。
dataSource的使用
Select 同时支持 children 和在 props 中传入 dataSource 作为数据源, 如果同时设置, 则以 children 为准.
注意:1. Select 默认使用 value
作为渲染的菜单项的 key
值,所以 value
不能重复, 否则无法渲染下拉菜单。2. value 不允许出现 null/undefined/object/array 类型数值
children
的方式
<Select>
<Select.Option value="option1">option1</Select.Option>
<Select.Option value="option2">option2</Select.Option>
<Select.Option disabled>disabled</Select.Option>
</Select>;
props
的方式
const dataSource = [
{label:'option1', value:'option1'},
{label:'option2', value:'option2'},
{label:'disabled', disabled:true}
];
<Select dataSource={dataSource}/>
定制弹出层
参见示例中的 弹层定制
。唯一需要注意的是 overlay
的元素记得透传 props.这是因为 Overlay 的弹层的动画是依靠 className
实现的,如果不透传 props 则会造成无法监听到动画播放结束的事件。
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 | 弹层挂载的容器节点 | String/Function | - |
popupClassName | 弹层的 className | any | - |
popupStyle | 弹层的内联样式 | Object | - |
popupProps | 添加到弹层上的属性 | Object | {} |
followTrigger | 是否跟随滚动 | Boolean | - |
popupContent | 自定义弹层的内容 | ReactNode | - |
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} 渲染节点的itemsearchValue: {String} 搜索关键字(如果开启搜索)返回值:{ReactNode} item node | Function | - |
mode | 选择器模式可选值:'single', 'multiple', 'tag' | Enum | 'single' |
notFoundContent | 弹层内容为空的文案 | ReactNode | - |
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 | 弹层挂载的容器节点 | String/Function | - |
popupClassName | 弹层的 className | any | - |
popupStyle | 弹层的内联样式 | Object | - |
popupProps | 添加到弹层上的属性 | Object | {} |
followTrigger | 是否跟随滚动 | Boolean | - |
popupContent | 自定义弹层的内容 | ReactNode | - |
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 | - |
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 | 关闭列表 |
代码示例
简单
查看源码在线预览
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);
标签模式,输入的内容可以作为选项
查看源码在线预览
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);
多选模式, 通过 showSearch
可以开启搜索, 但搜索值不可用作选项
查看源码在线预览
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);
多选模式
查看源码在线预览
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);
使用 Select 构建级联选择框
查看源码在线预览
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 的多种形态.
查看源码在线预览
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;
}
使用 OptionGroup 针对选项进行分组,也可以使用原生的 html 标签 optgroup
查看源码在线预览
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;
}
Select 的 value 可以是任意非空类型的值,但是要保证 toString() 后是唯一的。
查看源码在线预览
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);
使用 showSearch
显示搜索框,如果需要动态更新 dataSource,需要关闭 filterLocal
查看源码在线预览
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);
Select 增加前后缀
查看源码在线预览
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);
useDetailValue
把 value
从字符串变成对象
查看源码在线预览
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);
AutoComplete
继承了 Input
的能力,并在其基础上增加了 autoComplete 的功能。对于设置为AutoComplete
为off不生效对的情况,可以参考 MDN 中进行设置。
查看源码在线预览
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
大小、disabled、清除
查看源码在线预览
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, 设置 filterLocal
为 false
查看源码在线预览
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;
}
展示较为复杂的内容展示
查看源码在线预览
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"/> {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;
}
通过 itemRender
和 valueRender
(仅 Select) 自定义渲染的节点内容。
查看源码在线预览
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;
}
通过 popupContent 定制 Select 弹层, Select 使用 popupContent 中渲染出的 item 的 value 作为菜单项的key,如果没有提供或者自定义渲染 key 请使用 valueRender
查看源码在线预览
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;
}
select 配合无限滚动
查看源码在线预览
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" />
<Select useVirtual onChange={onChange} defaultValue="option50">
{generateData(100, true)}
</Select>
</div>
, mountNode);
当聚焦在组件上时,通过aria-labelledby
对组件进行描述。关于键盘操作请参考ARIA and KeyBoard
。
查看源码在线预览
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);