# Popover气泡卡片

点击/鼠标移入元素，弹出气泡式的卡片浮层。

## 使用场景

当目标元素有进一步的描述和相关操作时，可以收纳到卡片中，根据用户的操作行为进行展现。

和 `Tooltip` 的区别是，用户可以对浮层上的元素进行操作，因此它可以承载更复杂的内容，比如链接或按钮等。

## 案例演示

### 基本使用 string

children为简单的的string

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

ReactDOM.render(
  <Popover
    content="我是内容 string"
    direction="down"
  >
    基本使用
  </Popover>
  , _react_runner_);
```
---demoend

### 使用 ignoreHoverTriggerClick=false

通过设置 `ignoreHoverTriggerClick=false`，在触发模式为 hover 的场景下，点击 trigger，也不会关闭 pop

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

ReactDOM.render(
  <Popover
    content="我是内容 string"
    direction="down"
    ignoreHoverTriggerClick={false}
  >
    基本使用,点击时，弹出层也不会消失
  </Popover>
  , _react_runner_);
```
---demoend

### trigger设置自定义事件

`ignoreHoverTriggerClick=false` 时，可以执行trigger自定义的 click 事件

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

function click(){
  console.log('clicked!');
}

ReactDOM.render(
  <Popover
    content="我是内容..."
    direction="down"
    ignoreHoverTriggerClick={false}
  >
    <span onClick={click}>
      基本使用,点击时，弹出层也不会消失
    </span>
  </Popover>
  , _react_runner_);
```
---demoend

### trigger 设置 reClickStayOpen=true

`reClickStayOpen=true` 时，二次点击 Trigger 时，如果 pop 已打则不会关闭。

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

function click(){
  console.log('clicked!');
}

ReactDOM.render(
  <div>
    <Popover
      content="我是内容..."
      triggerMode="click"
      direction="down"
    >
      <span onClick={click}>
        默认模式，多次点击 “打开/关闭” 循环
      </span>
    </Popover>
    <div className="mt-20" />
    <Popover
      content="我是内容..."
      triggerMode="click"
      direction="down"
      reClickStayOpen
    >
      <span onClick={click}>
        弹出层显示后，再次点击不会关闭
      </span>
    </Popover>
  </div>
  , _react_runner_);
```
---demoend

### 基本使用 number

children为简单的的number

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

ReactDOM.render(
  <Popover
    content="我是内容 number"
    direction="down"
  >
    123456
  </Popover>
  , _react_runner_);
```
---demoend


### 基本使用 设置 style

children为简单的的string

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

ReactDOM.render(
  <Popover
    content="我是内容 string"
    direction="down"
    style={{ background: 'whitesmoke' }}
    contentWrapStyle={{ background: 'red' }}
  >
    基本使用
  </Popover>
  , _react_runner_);
```
---demoend

### 触发模式为点击

触发模式有 click 和 hover

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

ReactDOM.render(
  <Popover
    content="我是内容"
    direction="down"
    triggerMode="click"
  >
    点击我试试
  </Popover>
  , _react_runner_);
```
---demoend

### 关闭时销毁内容

---demo
```js
import { Popover, Icon } from 'amos-framework';

function click(){
  console.log('clicked!');
}

ReactDOM.render(
  <Popover
    content="内容..."
    direction="down"
    destroyContent
  >
    <span onClick={click}><Icon icon="eye" />popover-destory</span>
  </Popover>
  , _react_runner_);
```
---demoend

### 一直打开

---demo
```js
import { Popover, Icon } from 'amos-framework';

ReactDOM.render(
  <Popover
    content="内容..."
    direction="down"
    alwaysOpen
  >
    <span><Icon icon="eye" />popover-always open</span>
  </Popover>
  , _react_runner_);
```
---demoend

### 自定义 Container

指定外部节点作为 container 容器

---demo
```js
import { Popover, Icon } from 'amos-framework';

class Demo extends Component {
  render() {
    // 注意，Popover 组件卸载时，也会移出 this.container dom 节点
    return (
      <div style={{ position: 'relative' }}>
        <div ref={node => this.container = node} className="custom-pop-container" style={{ position: 'relative' }}/>
        <Popover
          content="自定义Container，内容..."
          direction="down"
          getPopContainer={() => this.container}
          fixed
          offsetY={20}
        >
          <div>
            <Icon icon="eye" />自定义 PopContainer
          </div>
        </Popover>
      </div>
    );
  }
}

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

### 自定义 Container 设置到 chidren 内

`Popover chidlren` 内容区内部的节点作为 `container`，即：将目标 `triggerNode` 内容区元素作为包裹容器。
---demo
```js
import { Popover, Icon } from 'amos-framework';

class Demo extends Component {
  render() {
    return (
      <div style={{ position: 'relative' }}>
        <Popover
          content="自定义Container，内容..."
          direction="down"
          getPopContainer={() => this.container}
          fixed
          offsetY={15}
        >
          <div>
            <Icon icon="eye" />自定义 PopContainer
            <div ref={node => this.container = node}/>
          </div>
        </Popover>
      </div>
    );
  }
}

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


### 嵌套使用

颞部嵌套

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

ReactDOM.render(
  <Popover
    content={(
      <Popover
        content="我是2层内容 string"
        direction="down"
      >
        我是一层内容也是二层触发
      </Popover>
    )}
    triggerMode="click"
    direction="down"
  >
    一层触发
  </Popover>
  , _react_runner_);
```
---demoend

### 自定义关闭

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

class MyContent extends Component {
  static contextTypes = {
    popoverContent: PropTypes.object
  };

  close = (e) => {
    // e.stopPropagation();
    const coo = this.context.popoverContent && this.context.popoverContent[Popover.OPENED_POPOVER];
    if (coo){
      coo.close();
    } else {
        const opic = Popover[Popover.OPENED_POPOVER];
        opic && opic.close();
    }
  }

  render() {
    return (
      <div>
        这是自定义的内容区域
        <Button onClick={this.close}>关闭</Button>
      </div>
    );
  }
}

ReactDOM.render(
  <Popover
    content={<MyContent />}
    triggerMode="click"
    direction="down"
  >
    自定义内容关闭
  </Popover>
  , _react_runner_);
```
---demoend

## props

| params | type | default | description |
| ------ | ------ | ------ | ------ |
| children | `string/ReactNode` | - | 触发 popover 元素，其中当children为简单的string或者number时，将采用span进行包裹 |
| triggerMode | string | hover | 触发方式，可选 `click/hover`。v1.10.0 之前版本，如果设置为 hover，单击trigger 节点时会关闭 Pop，v1.10.0 版本开始，hover模式下，给 trigger 添加 onClick 事件，用于阻止Pop 关闭，可通过设置 `ignoreHoverTriggerClick=true` 禁用该特效。 |
| style | Object | - | 自定义内联样式 |
| contentWrapStyle | Object | - | 弹出层自定义内联样式 |
| content | `string/ReactNode` | - | 卡片内容 |
| direction | string | up | 气泡方向，可选 `up/down/left/right` |
| align | string | - | 气泡位置，可选 `top/right/bottom/left/middle` |
| open | boolean | - | 可选参数，用于手动控制浮层显隐 |
| onToggle | function | - | 可选参数，用于转换浮层的显隐 |
| shouldOpen | function | - | 增强函数，用于判断是否显示浮层的高级判断 |
| shouldClose | function | - | 增强函数，用于判断是否关闭浮层的高级判断 |
| disabled | boolean | - | 禁止触发浮层的显隐 |
| aligned | boolean | - | 是否和内容对齐(设置浮层的宽与触发内容宽一致) |
| noWidth | boolean | - | 是否禁止设置 `PopContent` 的宽度，默认宽度将会和 `TrigerNode` 宽度一致。如 `Cascader` 中该项为 true |
| contentWrapClassName | string | `amos-popover-content` | pop 内容区自定义样式 |
| disableContentWrapper | boolean | - | 设置为 trie 则禁用 `contentWrapClassName` 样式名 |
| getPopContainer | `func: () => reactNode` | `() => { const div = document.createElement('div');document.body.appendChild(div); return div }` | 自定义容器，如果设置，则渲染到指定的节点。 |
| fixed | boolean | - | 固定模式，让子节点渲染在指定的 container 上时使用，往往自定义 `getPopContainer` 时，需要设置 fixed 为 true |
| offsetX | `number or string` | - | 表示 left 偏移值，在 fixed 模式下自定义调整 left 值 |
| offsetY | `number or string` | - | 表示 top 偏移值，在 fixed 模式下自定义调整 top 值 |
| ignoreHoverTriggerClick | boolean | true | 当 triggerMode=hover 时，默认会给 trigger node 添加 click 事件，用于阻止点击 trigger 时进行冒泡关闭 pop，部分场景下，如果在 Pop 外层有设置事件，此时可以设置 ignoreHoverTriggerClick 为 ture，用于激活外层事件。 since v1.10.0 及之后版本添加 |
| reClickStayOpen | boolean | false |  triggerMode=click 时，默认打开后再次点击 trigger node 时，会关闭 pop 层，如果设置该字段为 true，则二次点击 trigger node 时，不会关闭，只有点击外层才会关闭 pop。。 since v1.12.1 及之后版本添加 |

> 如果 fixed 模式无法解决部分场景需求，此时可以采用 [浮层 overlay](/#/framework/overlay) 解决。

## 注意

请确保 `Popover` 的子元素能接受 `onMouseEnter`、`onMouseLeave`、`onFocus`、`onClick` 事件。

`direction: 特性`：

down 和 up 在范围不够时，将会出现翻转。

left 和 right 在范围不够时，将会出现翻转。

> `Popover` 组件卸载时，也会移出 `getPopContainer` 方法返回的 dom 节点。

## 扩展

某些情况，需要直接使用 `SinglePop`

```js
import { Popover } from 'amos-framework';

const SinglePop = Popover.SinglePop;

class Demo extends Component {
  renderContent = () => {
    return (
      <ul>
        <li>菜单1</li>
        <li>菜单2</li>
        <li>菜单3</li>
      </ul>
    );
  }

  render(){
    return (
      <SinglePop
        className="mypop"
        trigger={<Avatar src={userPic} onClick={onAvatarClick} />}
        content={this.renderContent()}
      />
    );
  }
}
```

### 使用推荐

如果触发方式设置为 `hover` 时，此时点击 trigger 节点时，默认会关闭，可以通过设置 `ignoreHoverTriggerClick=true` ，开启 onClick 的冒泡，从而阻止关闭。

如:

```js
<Popover
  content="我是内容 string"
  direction="down"
  ignoreHoverTriggerClick
>
  基本使用
</Popover>
```

> 注意，该方式如果 `Popover` 外层组件需要接收 onClick 事件，则不能设置 `ignoreHoverTriggerClick`

如：

```js
// 该方式，如果设置 `ignoreHoverTriggerClick=true` 则上苍的 onClick 事件无法执行
<div onClick={handleClick}>
  <Popover
    content="我是内容 string"
    direction="down"
    ignoreHoverTriggerClick={false}
  >
    基本使用
  </Popover>
</div>

// 改造成如下方式即可：
<Popover
  content="我是内容 string"
  direction="down"
  ignoreHoverTriggerClick={false}
>
  <div onClick={handleClick}>
    基本使用
  </div>
</Popover>
```

## 已知bug及解决

### 在底部弹出pop，第二次点击展示时，left 值有错位？

当 `direction="up" align="left"` 时，

由于第一次打开 pop 会给 `popoverNode` 设置 left 和 top 值，此时如果 body 可以显示 scrollbar，再次打开 pop 时，通过 triggerNode 计算出的 left 值，由于滚动条的存在导致计算错误（17px 误差）。

解决办法：

```scss
// 方法一：直接给 body 禁用滚动 【不推荐】

body {
  overflow: hidden;
}
```

```js
// 方法二：关闭时，销毁外层node内容
```

```js
// 方法三：关闭时，还原node style （系统已内置 v1.11.9）
```
