# List

列表

## 使用场景

最基础的列表展示，可承载文字、列表、图片、段落，常用于后台数据展示页面。


### 基础列表

---demo
```js
import { List, Avatar } from 'amos-framework';

const randomData = (count = 5) => {
  const result = [];
  for (let i = 0; i < count; i++) {
    result.push({
      title: `amos-framework title ${i}`,
      id: i
    });
  }
  return result;
};

ReactDOM.render((
<List
  itemLayout="horizontal"
  dataSource={randomData(3)}
  renderItem={item => (
    <List.Item>
      <List.Item.Meta
        avatar={<Avatar src="https://tinypng.com/images/panda-chewing.png" />}
        title={<a href="/framework">{item.title}</a>}
        description="我是具体的内容区域，这块可以很长很长"
      />
    </List.Item>
  )}
/>
), _react_runner_);
```
---demoend

### 栅格列表

可以通过设置 `List` 的 `grid` 属性来实现栅格列表，`column` 可设置期望显示的列数。

---demo
```js
import { List, CardPane } from 'amos-framework';

const randomData = (count = 5) => {
  const result = [];
  for (let i = 0; i < count; i++) {
    result.push({
      title: `amos-framework title ${i}`,
      id: i
    });
  }
  return result;
};

ReactDOM.render((
<List
  grid={{ gutter: 16, column: 4 }}
  dataSource={randomData(5)}
  renderItem={item => (
    <List.Item>
      <CardPane title={item.title}>CardPane content</CardPane>
    </List.Item>
  )}
/>
), _react_runner_);
```
---demoend

### 滚动加载

---demo
```js
import { List, ScrollingLoad, Avatar, SingleSpin } from 'amos-framework';

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [],
      loading: false,
      hasMore: true
    };
  }

  componentDidMount() {
    this.getData(res => {
      this.setState({
        data: res.results
      });
    });
  }

  getData = callback => {
    // eslint-disable-next-line no-undef
    fetch('https://randomuser.me/api/?results=5&inc=name,gender,email,phone,nat&noinfo')
      .then(res => res.json())
      .then(data => callback(data));
  };

  handleInfiniteOnLoad = () => {
    let data = this.state.data;
    this.setState({
      loading: true
    });
    if (data.length > 14) {
      Toast.warning({
        title: '警告',
        content: '滚动加载器已经加载所有数据！'
      });
      this.setState({
        hasMore: false,
        loading: false
      });
      return;
    }
    this.getData(res => {
      data = data.concat(res.results);
      this.setState({
        data,
        loading: false
      });
    });
  };

  render() {
    return (
      <div style={{ height: 300, padding: '8px 24px', overflow: 'auto' }}>
        <ScrollingLoad
          initialLoad={false}
          pageStart={0}
          loadMore={this.handleInfiniteOnLoad}
          hasMore={!this.state.loading && this.state.hasMore}
          useWindow={false}
        >
          <List
            dataSource={this.state.data}
            renderItem={item => (
              <List.Item key={item.id}>
                <List.Item.Meta
                  avatar={<Avatar src="https://tinypng.com/images/panda-chewing.png" />}
                  title={<a href="/framework">{item.name.last}</a>}
                  description={item.email}
                />
                <div>内容区域</div>
              </List.Item>
            )}
          >
            {this.state.loading && this.state.hasMore ? (
              <div style={{ textAlign: 'center' }}>
                <SingleSpin>加载更多</SingleSpin>
              </div>
            ) : (
              <div style={{ textAlign: 'center', padding: '1em 0' }}>没有更多数据</div>
            )}
          </List>
        </ScrollingLoad>
      </div>
    );
  }
}

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

### 竖直与分页

通过设置 `itemLayout` 属性为 `vertical` 可实现竖排列表样式。

---demo
```js
import { List, ScrollingLoad, Avatar, SingleSpin, Icon } from 'amos-framework';

const randomData = (count = 5) => {
  const result = [];
  for (let i = 0; i < count; i++) {
    result.push({
      title: `amos-framework title ${i}`,
      id: i
    });
  }
  return result;
};

const totalDatas = randomData(20);

const calcPageData = (page = 'currentPage=1&pageSize=5') => {
  const [currentPage, pageSize] = page.split('&'); // 当前页、每页大小
  const cp = currentPage.split('=')[1];
  const size = pageSize.split('=')[1];

  const start = (parseInt(cp) - 1) * parseInt(size);
  const end = start + parseInt(size);

  return totalDatas.slice(start, end);
};

const IconText = ({ icon, text }) => (
  <span>
    <Icon icon={icon} style={{ marginRight: 8 }} />
    {text}
  </span>
);

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      pageData: calcPageData()
    };
  }

  handlePageChange = (page) => {
    const pageData = calcPageData(page);
    this.setState({
      pageData
    });
  }

  render() {
    return (
      <List
        itemLayout="vertical"
        size="large"
        pagination={{
          onChange: this.handlePageChange,
          pageSizeOptions: [5, 10, 15],
          pageSize: 5,
          totalPageNum: 20,
          currentPage: 1
        }}
        dataSource={this.state.pageData}
        footer={
          <div>
            <b>footer</b>
          </div>
        }
        renderItem={item => (
          <List.Item
            key={item.title}
            actions={[
              <IconText key="1" icon="collection" text="156" />,
              <IconText key="2" icon="star-hollow" text="156" />,
              <IconText key="3" icon="message" text="2" />
            ]}
            extra={<img width={100} alt="logo" src="https://tinypng.com/images/panda-chewing.png" />}
          >
            <List.Item.Meta avatar={
              <Avatar src="https://tinypng.com/images/panda-chewing.png" />}
              title={<a href={item.href}>{item.title}</a>}
              description={`描述信息 -- ${item.id}`}
            />
            {`内容区域 -- ${item.id}`}
          </List.Item>
        )}
      />
    );
  }
}

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

### LargeList 使用

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

const LargeList = List.LargeList;

const cnt = 5;

const renderItem = (index, key) => (
  <div key={key} className={`item${index % 2 ? '' : ' even'}`}>
    {index}
  </div>
);

const renderSquareItem = (index, key) => (
  <div key={key} className={`square-item${index % 2 ? '' : ' even'}`}>
    {index}
  </div>
);

const getHeight = index => 30 + 10 * (index % 10);

const getWidth = index => 100 + 10 * (index % 10);

const renderVariableHeightItem = (index, key) => (
  <div key={key} className={`item${index % 2 ? '' : ' even'}`} style={{ lineHeight: `${getHeight(index)}px` }}>
    {index}
  </div>
);

const renderVariableWidthItem = (index, key) => (
  <div key={key} className={`item${index % 2 ? '' : ' even'}`} style={{ width: `${getWidth(index)}px` }}>
    {index}
  </div>
);

const renderGridLine = (row, key) => (
  <LargeList axis="x" key={key} length={cnt} itemRenderer={(column, key) => renderSquareItem(column + cnt * row, key)} type="uniform" />
);

const examples = [
  {
    length: cnt,
    itemRenderer: renderVariableHeightItem
  },
  {
    axis: 'x',
    length: cnt,
    itemRenderer: renderVariableWidthItem
  },
  {
    length: cnt,
    itemRenderer: renderVariableHeightItem,
    type: 'variable'
  },
  {
    axis: 'x',
    length: cnt,
    itemRenderer: renderVariableWidthItem,
    type: 'variable'
  },
  {
    length: cnt,
    itemRenderer: renderVariableHeightItem,
    itemSizeGetter: getHeight,
    type: 'variable'
  },
  {
    axis: 'x',
    length: cnt,
    itemRenderer: renderVariableWidthItem,
    itemSizeGetter: getWidth,
    threshold: 0,
    type: 'variable'
  },
  {
    length: cnt,
    initialIndex: 2,
    itemRenderer: renderVariableHeightItem,
    itemSizeGetter: getHeight,
    type: 'variable'
  },
  {
    length: cnt,
    itemRenderer: renderItem,
    type: 'uniform'
  },
  {
    axis: 'x',
    length: cnt,
    itemRenderer: renderItem,
    type: 'uniform'
  },
  {
    length: cnt,
    itemRenderer: renderSquareItem,
    type: 'uniform'
  },
  {
    length: cnt,
    initialIndex: 5,
    itemRenderer: renderItem,
    type: 'uniform'
  },
  {
    length: cnt,
    itemRenderer: renderGridLine,
    type: 'uniform',
    useTranslate3d: true
  }
];

function renderExamples() {
  return examples.map((props, key) => (
    <div key={key} className={`example axis-${props.axis || ''}`}>
      <LargeList {...props} />
    </div>
  ));
}

ReactDOM.render((
<div className='examples'>{renderExamples()}</div>
), _react_runner_);
```
---demoend

### 选择列表

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

const SelectList = List.SelectList;

const randomData = (count = 5) => {
  const result = [];
  for (let i = 0; i < count; i++) {
    result.push({
      label: `af select list title ${i}`,
      key: i
    });
  }
  return result;
};

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      checkedKeys: []
    };
  }

  handleChange = (checkedKeys) => {
    this.setState({
      checkedKeys
    });
    console.log('checkedKeys', checkedKeys);
  }

  render() {
    const { checkedKeys } = this.state;
    return (
      <SelectList
        dataSource={randomData(10)}
        renderOption={item => item.label}
        checkedKeys={checkedKeys}
        onChange={this.handleChange}
        bordered
      />
    );
  }
}

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

### 单选列表

---demo
```js
import { List, Row, Col } from 'amos-framework';

const RadioList = List.RadioList;

const randomData = (count = 5) => {
  const result = [];
  for (let i = 0; i < count; i++) {
    result.push({
      label: `af select list title ${i}`,
      key: i
    });
  }
  return result;
};

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      selectKey: ''
    };
  }

  handleChange = (selectKey, info) => {
    this.setState({
      selectKey
    });
    console.log('selectKey | info', selectKey, info);
  }

  render() {
    const { selectKey } = this.state;
    return (
      <Row style={{ width: 800 }}>
        <Col span={8}>
          <RadioList
            dataSource={randomData(10)}
            renderOption={item => item.label}
            value={selectKey}
            onChange={this.handleChange}
            bordered
          />
        </Col>
        <Col span={14}>
          <RadioList
            dataSource={randomData(10)}
            renderOption={item => item.label}
            value={selectKey}
            onChange={this.handleChange}
            bordered
            mode="inline"
          />
        </Col>
      </Row>
    );
  }
}

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


### 单选列表 - 禁用某项选择

> 下方案例中，`key===2` 或者点击了 `禁止点击区域` 部位，均忽略选中。

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

const RadioList = List.RadioList;

const randomData = (count = 5) => {
  const result = [];
  for (let i = 0; i < count; i++) {
    result.push({
      label: `af select list title ${i}`,
      key: i
    });
  }
  return result;
};

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      selectKey: ''
    };
  }

  ignoreSelect = (dataItem, evt) => {
    if (dataItem.key === 2){
      return true;
    }
    if (evt.target.dataset.itemtype && evt.target.dataset.itemtype === 'not'){
      return true;
    }
    return false;
  }

  handleChange = (selectKey, info) => {
    this.setState({
      selectKey
    });
    console.log('selectKey | info', selectKey, info);
  }

  render() {
    const { selectKey } = this.state;
    return (
      <div style={{ width: 350 }}>
        <RadioList
          dataSource={randomData(10)}
          renderOption={item => <div>{item.label}<span style={{ color: 'red' }} data-itemtype="not">禁止点击区域</span></div>}
          value={selectKey}
          onChange={this.handleChange}
          ignoreSelect={this.ignoreSelect}
          bordered
        />
      </div>
    );
  }
}

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

## props

### List props

| params | type | default | description |
| ------- | ------ | ------ | ------ |
| bordered | boolean | false | 是否展示边框 |
| footer | string or ReactNode | - | 列表底部 |
| grid | object | - | 列表栅格配置 |
| header | string or ReactNode | - |
| itemLayout | string | - | 设置 `List.Item` 布局, 设置成 `vertical` 则竖直样式显示, 默认横排 |
| loading | boolean | false | 当卡片内容还在加载中时，可以用 `loading` 展示一个占位 |
| loadMore | string or ReactNode | - | 加载更多 |
| locale | object | emptyText: '暂无数据' | 默认文案设置，目前包括空数据文案 |
| pagination | `boolean or object` | false | 对应的 `pagination` 配置, 设置 `false` 不显示 |
| size | String | `default` | list 的尺寸，可选值 `default` 、 `middle` 、 `small` |
| split | boolean | true | 是否展示分割线 |

### pagination

分页的配置项。

| params | type | default | description |
| --- | --- | --- | --- |
| position | String | 'bottom' | 指定分页显示的位置，可选值有 'top' 、 'bottom' 、 'both' |

更多配置项，请查看 [`Pagination`](/framework/pagination)。

### List grid props

| params | type | default | description |
| --- | --- | --- | --- |
| column | number | - | 列数 |
| gutter | number | 0 | 栅格间隔 |
| xs | number | - | `<576px` 展示的列数 |
| sm | number | - | `≥576px` 展示的列数 |
| md | number | - | `≥768px` 展示的列数 |
| lg | number | - | `≥992px` 展示的列数 |
| xl | number | - | `≥1200px` 展示的列数 |
| xxl | number | - | `≥1600px` 展示的列数 |

### List.Item

| params | type | default | description |
| --- | --- | --- | --- |
| actions | `Array<ReactNode>` | - | 列表操作组，根据 `itemLayout` 的不同, 位置在卡片底部或者最右侧 |
| extra | string or ReactNode | - | 额外内容, 通常用在 `itemLayout` 为 `vertical` 的情况下, 展示右侧内容; `horizontal` 展示在列表元素最右侧 |

### List.Item.Meta

| params | type | default | description |
| --- | --- | --- | --- |
| avatar | ReactNode | - | 列表元素的图标 |
| description | string or ReactNode | - | 列表元素的描述内容 |
| title | string or ReactNode | - | 列表元素的标题 |

### SelectList props

| params | type | default | description |
| ------- | ------ | ------ | ------ |
| prefixCls | String | 'amos-selectlist' | 自定义样式前缀 |
| className | String | - | 自定义样式 |
| style | Object | - | 自定义内联样式 |
| dataSource | `Array<Object>` | - | 数据项 |
| checkedKeys | `Array<String>` | - | 选中的项 |
| onChange | `func: (checkedKeys, evt: {selectItem, checked}) => {}` | - | 数据选中切换回调 |
| renderOption | func | - | 自定义渲染 item |
| body | func | - | 自定义渲染面板 |
| lazy | `bool or Object` | - | 是否启用懒加载 |
| onScroll | func | - | 懒加载滚动时的回调 |
| bordered | bool | - | 是否显示边框 |


### RadioList props

| params | type | default | description |
| ------- | ------ | ------ | ------ |
| prefixCls | String | `amos-radiolist` | 自定义样式前缀 |
| className | String | - | 自定义样式 |
| style | Object | - | 自定义内联样式 |
| itemStyle | Object or `(dataItem) => {}` | - | 自定义 item 内联样式 |
| dataSource | `Array<Object>` | - | 数据项 |
| defaultValue | `String or Number` | - | 默认选中的项，不可控 |
| value | `String or Number` | - | 选中的项 |
| onChange | `func: (checkedKeys, evt: {event, dataItem}) => {}` | - | 数据选中切换回调 |
| renderOption | func | - | 自定义渲染 item |
| renderKey | `String or func: (item) => String` | `key` | 数据key |
| renderEmpty | `func: () => ReactNode` | - | 设置数据项为空时的渲染内容 |
| bordered | bool | - | 是否显示边框 |
| mode | String | block | 模式，可选值为 inline 和 block，设置 item 布局方式 |
| ignoreSelect | `func: (dataItem, evt) => Boolean` | - | 忽略选择 item 方法。返回值为 true 时，则忽略选中点击的 item, 也不会触发 onChange 事件。也可以在该方法中，处理阻止默认事件和事件冒泡，如： `evt.preventDefault() or evt.stopPropagation()`  |

