# 选项卡

## 使用

```js
import { Tabs, ComplexTab } from 'amos-framework';

const { TabList, Tab, TabPanel } = Tabs;

<Tabs>
  <TabList>
    <Tab>基本使用1</Tab>
    <Tab>基本使用2</Tab>
  </TabList>
  <TabPanel>我是 - 基本使用1</TabPanel>
  <TabPanel>我是 - 基本使用2</TabPanel>
</Tabs>
```

## 案例演示

### tabs 基本使用

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

const { TabList, Tab, TabPanel } = Tabs;

ReactDOM.render((
  <Tabs>
    <TabList>
      <Tab>Tabs1</Tab>
      <Tab>Tabs2</Tab>
    </TabList>
    <TabPanel>我是 - Tabs1</TabPanel>
    <TabPanel>我是 - Tabs2</TabPanel>
  </Tabs>
), _react_runner_);
```
---demoend

### tabs 初始加载所有

初始加载时，加载所有的 tab。可以更改未激活 tab 的 props 值，在切换时才进行数据更新。与 `destroyContent=true` 不可同时使用。`chrome F12` 查看 `Tabs2` 未激活状态下的内容。

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

const { TabList, Tab, TabPanel } = Tabs;

const InnerTab = ({ value }) => (
  <div>我是 - Tabs2 {value}</div>
);

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: '额外内容'
    };
  }

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

  render(){
    const { value } = this.state;
    return (
      <div>
        <Input value={value} onChange={this.handleChange} />
        <br />
        <br />
        <Tabs loadAllPanel>
          <TabList>
            <Tab>Tabs1</Tab>
            <Tab>Tabs2</Tab>
          </TabList>
          <TabPanel>我是 - Tabs1</TabPanel>
          <TabPanel alwaysUpdate><InnerTab value={value} /></TabPanel>
        </Tabs>
      </div>
    );
  }
}

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

### tabs 切换时销毁内容

切换时，非激活tab的dom内容将会被销毁.

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

const { TabList, Tab, TabPanel } = Tabs;

ReactDOM.render((
  <Tabs destroyContent>
    <TabList>
      <Tab>Tabs1</Tab>
      <Tab>Tabs2</Tab>
    </TabList>
    <TabPanel>我是 - Tabs1(切换时，在DevTools中查看相应dom)</TabPanel>
    <TabPanel>我是 - Tabs2(切换时，在DevTools中查看相应dom)</TabPanel>
  </Tabs>
), _react_runner_);
```
---demoend

### tabs 禁止切换动画

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

const { TabList, Tab, TabPanel } = Tabs;

ReactDOM.render((
  <Tabs closeAnimate>
    <TabList>
      <Tab>Tabs1</Tab>
      <Tab>Tabs2</Tab>
    </TabList>
    <TabPanel>我是 - Tabs1</TabPanel>
    <TabPanel>我是 - Tabs2</TabPanel>
  </Tabs>
), _react_runner_);
```
---demoend

### tabs position: right

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

const { TabList, Tab, TabPanel } = Tabs;

ReactDOM.render((
  <Tabs tabPosition="right" style={{ height: 100 }}>
    <TabList>
      <Tab>Tabs1</Tab>
      <Tab>Tabs2</Tab>
    </TabList>
    <TabPanel>我是 - Tabs1</TabPanel>
    <TabPanel>我是 - Tabs2</TabPanel>
  </Tabs>
), _react_runner_);
```
---demoend

### tabs position: left

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

const { TabList, Tab, TabPanel } = Tabs;

ReactDOM.render((
  <Tabs tabPosition="left" style={{ height: 100 }}>
    <TabList>
      <Tab>Tabs1</Tab>
      <Tab>Tabs2</Tab>
    </TabList>
    <TabPanel>我是 - Tabs1</TabPanel>
    <TabPanel>我是 - Tabs2</TabPanel>
  </Tabs>
), _react_runner_);
```
---demoend

### tabs position: bottom

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

const { TabList, Tab, TabPanel } = Tabs;

ReactDOM.render((
  <Tabs tabPosition="bottom">
    <TabList>
      <Tab>Tabs1</Tab>
      <Tab>Tabs2</Tab>
    </TabList>
    <TabPanel>我是 - Tabs1</TabPanel>
    <TabPanel>我是 - Tabs2</TabPanel>
  </Tabs>
), _react_runner_);
```
---demoend

### 设置 mode 模式

mode 可选值为 `card | mincard | line | bulge | unbulge | bullet | divider`。

注意： `bulge` 和 `unbulge` 唯一区别是：选中状态的tab颜色刚好相反。

---demo
```js
import { Tabs, Button } from 'amos-framework';

const { TabList, Tab, TabPanel } = Tabs;

const ButtonGroup = Button.Group;

const modes = ['card', 'mincard', 'line', 'bulge', 'unbulge', 'bullet', 'divider'];

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

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

  render(){
    const { mode } = this.state;
    return (
      <div>
        <ButtonGroup defaultValue="card" onChange={this.handleChange} tight>
          {
            modes.map(m => <Button key={m} value={m}>{m}</Button>)
          }
        </ButtonGroup>
        <br />
        <br />
        <Tabs mode={mode}>
          <TabList>
            <Tab>Tabs1</Tab>
            <Tab>Tabs2</Tab>
          </TabList>
          <TabPanel>我是 - Tabs1</TabPanel>
          <TabPanel>我是 - Tabs2</TabPanel>
        </Tabs>
      </div>
    );
  }
}

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

### 不同类型 mode 嵌套

测试时，可以嵌套多层进行测试不同类型 mode。

---demo
```js
import { Tabs, Button } from 'amos-framework';

const { TabList, Tab, TabPanel } = Tabs;

const ButtonGroup = Button.Group;

const InnerTab = ({ mode }) => (
<Tabs mode={mode}>
  <TabList>
    <Tab>Tabs1</Tab>
    <Tab>Tabs2</Tab>
  </TabList>
  <TabPanel>我是 - Tabs1</TabPanel>
  <TabPanel>我是 - Tabs2</TabPanel>
</Tabs>
);

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      mode: 'card',
      mode2: 'card'
    };
  }

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

  handleChange2 = (value) => {
    this.setState({
      mode2: value
    });
  }

  render(){
    const { mode, mode2 } = this.state;
    return (
      <div>
        <ButtonGroup defaultValue="card" onChange={this.handleChange} tight>
          <Button value="card">card</Button>
          <Button value="line">line</Button>
          <Button value="bulge">bulge</Button>
          <Button value="bullet">bullet</Button>
          <Button value="divider">divider</Button>
        </ButtonGroup>
        <span style={{ marginLeft: 20 }}>内部 Tab mode 变换：</span>
        <ButtonGroup defaultValue="card" onChange={this.handleChange2} tight>
          <Button value="card">card</Button>
          <Button value="line">line</Button>
          <Button value="bulge">bulge</Button>
          <Button value="bullet">bullet</Button>
          <Button value="divider">divider</Button>
        </ButtonGroup>
        <br />
        <br />
        <Tabs mode={mode}>
          <TabList>
            <Tab>Tabs1</Tab>
            <Tab>Tabs2</Tab>
          </TabList>
          <TabPanel><InnerTab mode={mode2} /></TabPanel>
          <TabPanel>我是 - Tabs2</TabPanel>
        </Tabs>
      </div>
    );
  }
}

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

### tabs 使用activeKey

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

const { TabList, Tab, TabPanel } = Tabs;

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

  handleChange = (index, key) => {
    this.setState({
      activeKey: key
    });
  }

  render(){
    const { activeKey } = this.state;
    return (
      <Tabs activeKey={activeKey} onChange={this.handleChange}>
        <TabList>
          <Tab activeKey="data1">选项卡1</Tab>
          <Tab activeKey="data2">选项卡2</Tab>
        </TabList>
        <TabPanel activeKey="data1">选项卡1 内容</TabPanel>
        <TabPanel activeKey="data2">选项卡2 内容</TabPanel>
      </Tabs>
    );
  }
}

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

### tabs 使用activeKey初始为 null 时，则默认选中第一个

> 往往用于动态 tabs 中，不知道具体的 activeKey 的场景，可以默认选中第一个

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

const { TabList, Tab, TabPanel } = Tabs;

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      // 初始时 null
      activeKey: null
    };
  }

  handleChange = (index, key) => {
    this.setState({
      activeKey: key
    });
  }

  render(){
    const { activeKey } = this.state;
    return (
      <Tabs activeKey={activeKey} autoActiveFirst onChange={this.handleChange}>
        <TabList>
          <Tab activeKey="data1">选项卡1</Tab>
          <Tab activeKey="data2">选项卡2</Tab>
        </TabList>
        <TabPanel activeKey="data1">选项卡1 内容</TabPanel>
        <TabPanel activeKey="data2">选项卡2 内容</TabPanel>
      </Tabs>
    );
  }
}

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

### tabs 使用 activeIndex (不推荐)

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

const { TabList, Tab, TabPanel } = Tabs;

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

  handleChange = (index, key) => {
    this.setState({
      activeIndex: index
    });
  }

  render(){
    const { activeIndex } = this.state;
    return (
      <Tabs activeIndex={activeIndex} onChange={this.handleChange}>
        <TabList>
          <Tab>选项卡1</Tab>
          <Tab>选项卡2</Tab>
        </TabList>
        <TabPanel>选项卡1 内容</TabPanel>
        <TabPanel>选项卡2 内容</TabPanel>
      </Tabs>
    );
  }
}

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

### tabs 动态tabs

---demo
```js
import { Button, Tabs } from 'amos-framework';

const { TabList, Tab, TabPanel } = Tabs;
let newUid = 2;

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      tabs: [{
        id: 'A',
        title: '默认',
        content: '默认(不可关闭)',
        closable: false
      }, {
        id: 'B',
        title: 'tab1',
        content: 'content tab1'
      }]
    };
  }

  handleChange = (activeIndex, key) => {
    this.setState({
      activeIndex
    });
  }

  handleAdd = () => {
    const tabs = this.state.tabs;
    const id = newUid++;
    tabs.push({
      id,
      title: `tab${id}`,
      content: `content tab${id}`
    });
    this.setState({ tabs, activeIndex: tabs.length - 1 });
  }

  handleClose = (index) => {
    const tabs = this.state.tabs;
    tabs.splice(index, 1);
    this.setState({ tabs, activeIndex: tabs.length - 1 });
  }

  render(){
    const { activeIndex, tabs } = this.state;
    return (
      <Tabs
        dynamic
        activeIndex={activeIndex}
        handleClose={this.handleClose}
        onChange={this.handleChange}
      >
        <TabList extra={<Button transparent useInnerIcon icon="add" onClick={this.handleAdd} />}>
          {tabs.map((item, i) => (
            <Tab key={item.id} closable={item.closable}>{item.title}</Tab>)
          )}
        </TabList>
        {tabs.map((item, i) => (
          <TabPanel key={item.id}>{`我是${item.content}`}</TabPanel>
        ))}
      </Tabs>
    );
  }
}

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

### tabs 动态tabs ComplexTab

---demo
```js
import { Button, Tabs } from 'amos-framework';

const { TabList, Tab, TabPanel } = Tabs;
let cpUid = 2;

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      complexTabActiveKey: 'ct0',
      complexTabs: [
        { key: 'ct0', title: 'ct0', content: '默认' },
        { key: 'ct1', title: 'ct1', content: '内容呀' }
      ]
    };
  }

  onComplexTabChange = (index, key) => {
    this.setState({
      complexTabActiveKey: key
    });
  }

  addComplexTab = () => {
    const tabs = this.state.complexTabs;
    const id = cpUid++;
    const key = `ct${id}`;
    tabs.push({
      key,
      title: key,
      content: `内容呀 tab${id}`
    });
    this.setState({ complexTabs: tabs, complexTabActiveKey: key });
  }

  delComplexTab = () => {
    const { complexTabs, complexTabActiveKey } = this.state;
    const newTabs = complexTabs.filter(ct => ct.key !== complexTabActiveKey);
    this.setState({
      complexTabs: newTabs,
      complexTabActiveKey: newTabs.length > 0 ? newTabs[0].key : ''
    });
  }

  handleComplexTabClose = (index, activeKey) => {
    const { complexTabs } = this.state;
    const newTabs = complexTabs.filter(ct => ct.key !== activeKey);
    this.setState({
      complexTabs: newTabs,
      complexTabActiveKey: newTabs[0].key
    });
  }

  renderTabNav = (item, index) => {
    return <div>{item.title}</div>;
  }

  renderTabPage = (item, index) => {
    return <div>{item.content}</div>;
  }

  render(){
    const { complexTabs, complexTabActiveKey } = this.state;
    const extra = [
      <Button key="add" transparent useInnerIcon icon="add" onClick={this.addComplexTab} />,
      <Button key="del" transparent useInnerIcon icon="delete" onClick={this.delComplexTab} />
    ];
    return (
      <ComplexTab
        showArrow
        hideCloseIcon
        tabs={complexTabs}
        extra={extra}
        activeKey={complexTabActiveKey}
        renderTabNav={this.renderTabNav}
        renderTabPage={this.renderTabPage}
        onTabChange={this.onComplexTabChange}
        onTabClose={this.delComplexTab}
      />
    );
  }
}

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

### tabs 与其它组件联合使用

很多场景下，`tabs header` 需要与其它组件联合使用。本示例仅仅是演示功能，具体的样式效果需要自行调整。

---demo
```js
import { Tabs, Input, Button, Radio, Switch, Avatar, Badge, Tooltip, Editable } from 'amos-framework';

const { TabList, Tab, TabPanel } = Tabs;
const ButtonGroup = Button.Group;

const InnerTab = ({ value }) => (
  <div>我是 - Tabs2 {value}</div>
);

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      checked: false,
      checkedRadio: false,
      onOff: true,
      tabTitle: 'Tabs1',
      stopEvt: false,
      innerType: 'Checkbox'
    };
  }

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

  changeStopEvt = (evt) => {
    this.setState({
      stopEvt: evt.target.checked
    });
  }

  clickCheckbox = (evt) => {
    // 如果切换 checkbox 时，不切换 tabs 选中，只需要阻止事件冒泡即可
    if (this.state.stopEvt){
      evt.stopPropagation();
    }
    this.setState((prevState) => ({
      checked: !prevState.checked
    }));
  }

  clickRadio = (evt) => {
    if (this.state.stopEvt){
      evt.stopPropagation();
    }
    this.setState((prevState) => ({
      checkedRadio: !prevState.checkedRadio
    }));
  }

  onOffChange = (onOff) => {
    this.setState({
      onOff
    });
  }

  btnClick = (evt) => {
    if (this.state.stopEvt){
      evt.stopPropagation();
    }
    // 如果切换 checkbox 时，不切换 tabs 选中，只需要阻止事件冒泡即可
    // evt.stopPropagation();
    console.log('btnClick');
  }

  editTabHeader = value => {
    this.setState({
      tabTitle: value
    });
  }

  renderHeader(){
    const { innerType, checked, checkedRadio, onOff, tabTitle } = this.state;
    switch (innerType) {
      case 'Checkbox':
        return <Checkbox checked={checked} onClick={this.clickCheckbox} />
        // return <Checkbox checked={checked} onClick={this.clickCheckbox}>测试</Checkbox>
      case 'Button':
        return <Button onClick={this.btnClick}>按钮</Button>
      case 'Input':
        return <Input style={{ color: 'black' }}/>
      case 'Radio':
        return <Radio checked={checkedRadio} onClick={this.clickRadio} />
      case 'Switch':
        return <Switch defaultOn onOff={onOff} onChange={this.onOffChange} />;
      case 'Avatar':
        return <Avatar useInnerIcon size="sm" icon="user" />;
      case 'Badge':
        return <Badge count={25} />;
      case 'Tooltip':
        return <Tooltip title="你好呀">tooltip</Tooltip>
      case 'Editable':
        return <Editable inputProps={{ style: { color: 'black' } }} value={tabTitle} onChange={this.editTabHeader} showOkCancel={false} />
      default:
        break;
    }
    return '默认';
  }

  render(){
    const { value, tabTitle, innerType, stopEvt } = this.state;
    return (
      <div>
        <span style={{ marginLeft: 20 }}>header额外渲染类型：</span>
        <ButtonGroup defaultValue="Checkbox" onChange={this.handleChange} tight>
          <Button value="Checkbox">Checkbox</Button>
          <Button value="Button">Button</Button>
          <Button value="Input">Input</Button>
          <Button value="Radio">Radio</Button>
          <Button value="Switch">Switch</Button>
          <Button value="Avatar">Avatar</Button>
          <Button value="Badge">Badge</Button>
          <Button value="Tooltip">Tooltip</Button>
          <Button value="Editable">Editable</Button>
        </ButtonGroup>
        <br />
        <br />
        <Checkbox checked={stopEvt} onChange={this.changeStopEvt} />阻止事件冒泡（额外header不会触发 tabs 切换）
        <br />
        <br />
        <Tabs>
          <TabList>
            <Tab>{this.renderHeader()}{innerType === 'Editable' ? '' : tabTitle}</Tab>
            <Tab>Tabs2</Tab>
          </TabList>
          <TabPanel>我是 - Tabs1</TabPanel>
          <TabPanel>我是 - Tabs2</TabPanel>
        </Tabs>
      </div>
    );
  }
}

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

## API

### Tabs

| params | type |  default | description |
| ------ | ------ | ------ | ------ |
| prefixCls | string | `amos-tabs` | css class 前缀 |
| style | object | - | inline style |
| children | ReactNode | - | `TabPanel/TabPanel` tab内容 |
| tabPosition | string | - | `'left', 'right', 'top', 'bottom'` tab位置 |
| className | string | - | 自定义className |
| activeIndex | number | - | 指定（索引值）某个 Tab 处于 active 状态 |
| activeKey | string | - | 指定（key值）某个 Tab 处于 active 状态 |
| onChange | function | - | Tab 切换后的回调，参数(index, key) |
| dynamic | boolean | - | 是否开启可关闭模式，并切换不同的选项卡样式 |
| handleClose | function | - | Tab 关闭事件处理，参数(index, key) |
| closeAnimate | boolean | true | 是否启用tab切换时动画过度 |
| ~~cardMode~~ | boolean | false | 采用 卡片模式标签页，样式效果仅支持 `tabPosition=top` 的场景 |
| mode | `Enum {'card','mincard','line','bulge','unbulge','bullet','divider'}` | - | 卡片模式，card 模式，line 模式, bulge 模式 （选中翘起），bullet 模式（子弹头），divider 分割线 |
| destroyContent | boolean | false | 当tab切换时，非激活的tab内容是否销毁，默认不销毁 |
| loadAllPanel | boolean | false | 初始时加载所有的 tabpanel，注意，未激活的 tabPanel，不进行更新，当激活时会统一进行更新 |
| autoActiveFirst | boolean | - | 如果设置 activeKey 方式进行选中，初始不传入 key 时，设置该参数为 true，可自动选中第一个。注意，该模式不能用 `null` 作为 tab 的 activeKey |

> 注意：Tabs 默认以索引来管理 active 的状态，但是你也可以给每个 Tab 以及 TabPanel 绑定 "key"，这里用 activeKey 表示，然后管理 Tabs 的 activeKey 状态来控制选项卡的 active 状态。但是，`activeKey`与`activeIndex`不能同时存在。

### Tabs.TabList

| params | type |  default | description |
| ------ | ------ | ------ | ------ |
| prefixCls | string | `amos-tabs` | css class 前缀 |
| children | ReactNode | - | `Tab` tab头内容 |
| className | string | - | 自定义className |
| preExtra | ReactNode or `() => ReactNode` | - | 在选项卡标题列之前显示的内容 |
| extra | ReactNode or `() => ReactNode` | - | 在选项卡标题列之后显示的内容 |

> 合理使用 `preExtra` 和 `extra`，给选项卡添加扩展功能。`TabList` 中如果直接添加其它 `ReactElement`，最终将会在 `ul` 标签下生成相应的 `html` 元素。
> 高级浏览器中 `ul` 支持任意元素作为 `child`，部分浏览器将会出现非 `li` 元素的兼容性问题。

* 自由组合 `TabList`，实现自定义 tab header

```js
<Tabs
  ...
  handleClose={this.handleClose}
  onChange={this.handleChange}
>
  <div className="tab-header">
    <div>tab header 之前的内容</div>
    <div className="tab-header-nav">
      <TabList extra={<Button transparent useInnerIcon icon="add" onClick={this.handleAdd} />}>
        {tabs.map((item, i) => (
          <Tab key={item.id} closable={item.closable}>{item.title}</Tab>)
        )}
      </TabList>
    </div>
    <div>tab header 之后的内容</div>
  </div>
  <div className="tab-conent">
    {tabs.map((item, i) => (
      <TabPanel key={item.id}>{`我是${item.content}`}</TabPanel>
    ))}
  </div>
</Tabs>
```

### Tabs.Tab

| params | type |  default | description |
| ------ | ------ | ------ | ------ |
| prefixCls | string | `amos-tabs` | css class 前缀 |
| children | ReactNode | - | tab头内容 |
| className | string | - | 自定义className |
| style | Object | - | 自定义内联样式 |
| tabInnerStyle | Object | - | tab inner 自定义内联样式 |
| activeKey | string | - | 与 Tabs activeKey 对应 |
| closable | boolean | true | 当 Tabs 为 dynamic 时，是否可关闭，默认可关闭 |

### Tabs.TabPanel

| params | type |  default | description |
| ------ | ------ | ------ | ------ |
| prefixCls | string | `amos-tabs` | css class 前缀 |
| children | ReactNode | - | tab面板内容 |
| className | string | - | 自定义className |
| style | Object | - | 自定义内联样式 |
| activeKey | string | - | 与 Tabs activeKey 对应 |
| alwaysUpdate | bool | - | 配合 loadAllPanel，如果相应的 TabPanel 设置了 alwaysUpdate=true 则即使指定的 TabPanel 未激活，也执行更新，只有在 `loadAllPanel` 模式下才有效果 |
