Transfer  穿梭框

开发指南#

何时使用#

用直观的方式在两栏中移动元素,完成选择行为。

API#

Title
Title
import { Transfer } from '@alifd/next';

const dataSource = (() => {
    const dataSource = [];

    for (let i = 0; i < 10; i++) {
        dataSource.push({
            label: `content${i}`,
            value: `${i}`,
            disabled: i % 4 === 0
        });
    }

    return dataSource;
})();

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

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

    handleChange(value, data, extra) {
        console.log(value, data, extra);
    }

    render() {
        return (
            <Transfer defaultValue={['3']} dataSource={dataSource} defaultLeftChecked={['1']} onChange={this.handleChange} titles={['Title', 'Title']} />
        );
    }
}

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

最简单的用法。

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

const dataSource = (() => {
    const dataSource = [];

    for (let i = 0; i < 10; i++) {
        dataSource.push({
            label: `content${i}`,
            value: `${i}`,
            disabled: i % 4 === 0
        });
    }

    return dataSource;
})();

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

        this.state = {
            value: ['3']
        };

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

    handleChange(value, data, extra) {
        console.log(value, data, extra);

        this.setState({
            value
        });
    }

    render() {
        return <Transfer value={this.state.value} dataSource={dataSource} defaultLeftChecked={['1']} onChange={this.handleChange} titles={['Title', 'Title']} />;
    }
}

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

展示受控的用法。

code collapse
Simple Mode
Simple Mode
import { Transfer } from '@alifd/next';

const dataSource = (() => {
    const dataSource = [];

    for (let i = 0; i < 10; i++) {
        dataSource.push({
            label: `content${i}`,
            value: `${i}`,
            disabled: i % 4 === 0
        });
    }

    return dataSource;
})();

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

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

    handleChange(value, data, extra) {
        console.log(value, data, extra);
    }

    render() {
        return <Transfer mode="simple" defaultValue={['3']} dataSource={dataSource} defaultLeftChecked={['1']} onChange={this.handleChange} titles={['Simple Mode', 'Simple Mode']} />;
    }
}

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

通过设置 mode 为 'simple',可以开启简单模式,点击选项即移动。

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

const dataSource = (() => {
    const dataSource = [];

    for (let i = 0; i < 10; i++) {
        dataSource.push({
            label: `content${i}`,
            value: `${i}`,
            disabled: i % 4 === 0
        });
    }

    return dataSource;
})();

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

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

    handleChange(value, data, extra) {
        console.log(value, data, extra);
    }

    render() {
        return <Transfer showSearch defaultValue={['3']} dataSource={dataSource} defaultLeftChecked={['1']} onChange={this.handleChange} titles={['Searchable', 'Searchable']} />;
    }
}

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

展示搜索的用法。

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

const dataSource = (() => {
    const dataSource = [];

    for (let i = 0; i < 10; i++) {
        dataSource.push({
            label: `content${i}`,
            value: `${i}`,
            disabled: i % 4 === 0
        });
    }

    return dataSource;
})();

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

        this.handleSort = this.handleSort.bind(this);
    }

    handleSort(value, position) {
        console.log(value, position);
    }

    render() {
        return <Transfer sortable defaultValue={['3']} dataSource={dataSource} onSort={this.handleSort} titles={['Sortable', 'Sortable']} />;
    }
}

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

设置 sortable 属性为 true 后,可拖拽排序左右面板。

code collapse
Target
import { Transfer, Button } from '@alifd/next';

const dataSource = (() => {
    const dataSource = [];

    for (let i = 0; i < 10; i++) {
        dataSource.push({
            label: i % 3 === 0 ? `content${i}contentcontentcontentcontentcontent` : `content${i}`,
            value: `${i}`,
            disabled: i % 4 === 0
        });
    }

    return dataSource;
})();

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

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

    handleChange(value, data, extra) {
        console.log(value, data, extra);
    }

    render() {
        return (
            <Transfer 
                defaultValue={['3']} 
                dataSource={dataSource} 
                listStyle={{ width: '200px', height: '192px' }} 
                defaultLeftChecked={['1']} 
                onChange={this.handleChange} 
                titles={[<Button key='left' type='primary'>Source</Button>, 'Target']} 
                operations={['>>', '<<']} />
        );
    }
}

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

展示自定义样式的用法。

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

const dataSource = (() => {
    const dataSource = [];

    for (let i = 0; i < 10; i++) {
        dataSource.push({
            label: `content${i}`,
            value: `${i}`,
        });
    }

    return dataSource;
})();

const obj = {
    items: '项',
    item: '项',
    moveAll: '移动全部',
    searchPlaceholder: '请输入',
    moveToLeft: '撤销选中元素',
    moveToRight: '提交选中元素'
};

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

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

    handleChange(value, data, extra) {
        console.log(value, data, extra);
    }

    render() {
        return (
            <Transfer id="a11y-transfer" defaultValue={['2']} dataSource={dataSource} defaultLeftChecked={['1']} locale={obj} onChange={this.handleChange} titles={['Title', 'Title']} />
        );
    }
}

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

通过设置locale去修改对无障碍支持,默认已经设置,请参考ARIA and KeyBoard。 为保证可访问性,需要设置全局唯一的id

code collapse
Target
Not Found
import { Transfer, Button, Tree } from '@alifd/next';

const TreeNode = Tree.Node;

const treeDataSource = [{
  label: 'Form',
  key: '2',
  value: '2',
  children: [{
      label: 'Input',
      key: '4',
      value: '4'
  }, {
      label: 'Field',
      key: '7',
      value: '7'
  }, {
      label: 'Select',
      key: '5',
      value: '5',
  }]
}, {
  label: 'Display',
  key: '3',
  value: '3',
  children: [{
      label: 'Table',
      key: '6',
      value: '6'
  }]
}, {
  label: 'Data',
  key: '8',
  value: '8',
}];

const transferDataSource = [];
function flatten(list = []) {
    list.forEach(item => {
      transferDataSource.push(item);
      flatten(item.children);
    });
}
flatten(treeDataSource);


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

        this.handleChange = this.handleChange.bind(this);
        this.state = {
            selected: []
        };
    }

    handleChange(value, data, extra) {
        this.setState({ selected: value });
    }

    onCheck(keys, info) {
    }

    getTreeDataSource(dataSource = [], value) {
        return dataSource.map(({ children, ...props }) => (
            <TreeNode {...props} disabled={props.disabled || value.includes(props.value)} key={props.value}>
                {this.getTreeDataSource(children, value)}
            </TreeNode>
        ));
    }

    render() {
        const { onChange } = this.props;
        const { selected } = this.state;

        return (
            <Transfer
                dataSource={transferDataSource}
                listStyle={{ width: '200px', height: '192px' }}
                onChange={this.handleChange}
                titles={[<Button key='left' type='primary'>Source</Button>, 'Target']} >
                { ({ position, onChange, value }) => {
                    if (position === 'left') {
                        return (
                            <Tree checkable editable
                                style={{padding: '10px'}}
                                checkedKeys={value}
                                onCheck={
                                    (keys, extra) => {
                                        const newValues=extra.checkedNodes.map(item => item.props.value);
                                        onChange(position, newValues);
                                    }
                                }>
                                {this.getTreeDataSource(treeDataSource, selected)}
                            </Tree>
                        );
                    }
                  }
                }
            </Transfer>
        );
    }
}

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

展示自定义面板的用法。

code collapse

# API

Transfer#

参数 说明 类型 默认值
mode 移动选项模式

可选值:
'normal', 'simple'
Enum 'normal'
dataSource 数据源 Array<Object> []
value (用于受控)当前值 Array<String> -
defaultValue (用于非受控)初始值 Array<String> []
onChange 值发生改变的时候触发的回调函数

签名:
Function(value: Array, data: Array, extra: Object) => void
参数:
value: {Array} 右面板值
data: {Array} 右面板数据
extra: {Object} 额外参数
extra.leftValue: {Array} 左面板值
extra.leftData: {Array} 左面板数据
extra.movedValue: {Array} 发生移动的值
extra.movedData: {Object} 发生移动的数据
extra.direction: {String} 移动的方向,值为'left'或'right'
Function -
disabled 是否禁用 Boolean false
leftDisabled 是否禁用左侧面板 Boolean false
rightDisabled 是否禁用右侧面板 Boolean false
itemRender 列表项渲染函数

签名:
Function(data: Object) => ReactNode
参数:
data: {Object} 数据
返回值:
{ReactNode} 列表项内容
Function data => data.label
showSearch 是否显示搜索框 Boolean false
filter 自定义搜索函数

签名:
Function(searchedValue: String, data: Object) => Boolean
参数:
searchedValue: {String} 搜索的内容
data: {Object} 数据
返回值:
{Boolean} 是否匹配到
Function 根据 label 属性匹配
onSearch 搜索框输入时触发的回调函数

签名:
Function(searchedValue: String, position: String) => void
参数:
searchedValue: {String} 搜索的内容
position: {String} 搜索面板的位置
Function () => {}
searchPlaceholder 搜索框占位符 String -
notFoundContent 列表为空显示内容 ReactNode 'Not Found'
titles 左右面板标题 Array<ReactNode> []
operations 向右向左移动按钮显示内容 Array<ReactNode> [<Icon type="arrow-right" />, <Icon type="arrow-left" />]
defaultLeftChecked 左面板默认选中值 Array<String> []
defaultRightChecked 右面板默认选中值 Array<String> []
listClassName 左右面板列表自定义样式类名 String -
listStyle 左右面板列表自定义样式对象 Object -
sortable 是否允许拖拽排序 Boolean false
onSort 拖拽排序时触发的回调函数

签名:
Function(value: Array, position: String) => void
参数:
value: {Array} 排序后的值
position: {String} 拖拽的面板位置,值为:left 或 right
Function () => {}
id 请设置 id 以保证transfer的可访问性 String -
children 接收 children 自定义渲染列表

签名:
Function() => void
Function -

ARIA and KeyBoard#

按键 说明
Up Arrow 获取当前项的前一项焦点
Down Arrow 获取当前项的后一项焦点
Enter 当前列表选中的项移动到另一个列表
SPACE 选择/取消当前项或当前列表选中的项移动到另一个列表