# Modal

模态对话框。

需要用户处理事务，又不希望跳转页面以致打断工作流程时，可以使用 Modal 在当前页面正中打开一个浮层，承载相应的操作。

## Usage

### Basic

```tsx
import React, { useState } from 'react'
import { Button, Modal } from 'react-icecream'

export default () => {
  const [visible, setVisible] = useState(false)

  return (
    <>
      <Button onClick={() => setVisible(true)}>打开弹框</Button>
      <Modal
        title="基本示例"
        visible={visible}
        onOk={() => setVisible(false)}
        onCancel={() => setVisible(false)}
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Modal>
    </>
  )
}
```

### 可见状态（`visible`）非受控的弹框

```tsx
import React, { useState } from 'react'
import { Button, Modal } from 'react-icecream'

export default () => {
  const [shouldRender, setShouldRender] = useState(false)

  return (
    <>
      <Button onClick={() => setShouldRender(true)}>渲染弹框</Button>
      {shouldRender && (
        <Modal title="非受控弹框">
          <p>Some contents...</p>
          <p>Some contents...</p>
          <p>Some contents...</p>
        </Modal>
      )}
    </>
  )
}
```

### 不可见时自动销毁

```tsx
import React, { useState, useEffect, useRef } from 'react'
import { Modal, Button } from 'react-icecream'

const Content = () => {
  const [count, setCount] = useState(0)
  const total = useRef(0)

  useEffect(() => {
    const interval = setInterval(() => {
      total.current += 1
      setCount(total.current)
    }, 1000)
    return () => clearInterval(interval)
  }, [])

  return <span>{count}</span>
}

export default () => {
  const [visible, setVisible] = useState(false)

  return (
    <>
      <Button onClick={() => setVisible(true)}>打开弹框</Button>
      <Modal
        visible={visible}
        title="弹框关闭时销毁"
        onOk={() => setVisible(false)}
        onCancel={() => setVisible(false)}
        autoDestroy
      >
        <Content />
      </Modal>
    </>
  )
}
```

### Loading

`onOk` 返回一个 Promise，则主要按钮会切换到 `loading` 状态

```tsx
import React, { useState } from 'react'
import { Button, Modal } from 'react-icecream'

export default () => {
  const [visible, setVisible] = useState(false)

  async function handleOk() {
    await new Promise(r => setTimeout(() => r(), 3000))
    setVisible(false)
  }

  return (
    <>
      <Button onClick={() => setVisible(true)}>打开弹框</Button>
      <Modal
        title="基本示例"
        visible={visible}
        onOk={handleOk}
        onCancel={() => setVisible(false)}
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Modal>
    </>
  )
}
```

### Modal 搭配 Context API

```tsx
import React, { useState, createContext, useContext } from 'react'
import { Button, Modal } from 'react-icecream'

const context = createContext()

function ModalChild() {
  return <p>{useContext(context)}</p>
}

export default () => {
  const [visible, setVisible] = useState(false)

  return (
    <>
      <Button onClick={() => setVisible(true)}>打开弹框</Button>
      <context.Provider value="这是来自 context 的 value">
        <Modal
          title="搭配 Context API"
          visible={visible}
          onOk={() => setVisible(false)}
          onCancel={() => setVisible(false)}
        >
          <ModalChild />
        </Modal>
      </context.Provider>
    </>
  )
}
```

### 对 `footer` 进行配置

通过对传入 prop `footer` 的 `Footer` 进行配置，我们可以控制 `footer` 中按钮的文本、类型等

<code src="./demos/footer.tsx"></code>

### 自定义 `Footer` 组件

你也可以实现完全自定义的 `Footer` 组件，注意在自定义的 `Footer` 组件中可以通过 `useModalFooterCtx` 获得当前弹框的信息

<code src="./demos/custom-footer.tsx"></code>

### 带 Form

这里以展示样式为目的

<code src="./demos/form-in-modal.tsx"></code>

### 带 Table

<code src="./demos/table-in-modal.tsx"></code>

### 点击 mask 关闭

<code src="./demos/mask-closable.tsx"></code>

### 外部套了 PopupContainerProvider 仍然不会影响 popup 的效果

`Modal` 会 append 到 body 最后的字节点位置，而此时如果提供了 `PopupContainerProvider`，
使其在当前 dom 下生成弹窗，则会导致按顺序计算覆盖方式(z-index 相同)，即后者覆盖前者。
`Modal` 通过自带 PopupContainerProvider 的方式让其子节点的弹窗渲染到 `Modal` 下面可规避此类问题。

<code src="./demos/dropdown.tsx"></code>

### 垂直居中

```tsx
import React, { useState } from 'react'
import { Button, Modal } from 'react-icecream'

export default () => {
  const [visible, setVisible] = useState(false)

  return (
    <>
      <Button onClick={() => setVisible(true)}>打开弹框</Button>
      <Modal
        title="基本示例"
        visible={visible}
        onOk={() => setVisible(false)}
        onCancel={() => setVisible(false)}
        centered
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Modal>
    </>
  )
}
```

### 内容区域很高，Modal 能上下滚动

<code src="./demos/modal-scroll.tsx"></code>

<code src="./demos/dev/tooltip.tsx"></code>

<API src="./Modal.tsx" exports='["default"]'></API>
