Skip to content

@overlastic/react

@overlastic/react 用于在 React 中定义 Overlays 组件,并支持命令式与声明式的使用!

Install

With pnpm:

sh
pnpm add @overlastic/react

With yarn:

sh
yarn add @overlastic/react

Usage

步骤.1: Define Component

tsx
// overlay.tsx
export function OverlayComponent(props) {
  const { visible, resolve, reject } = useOverlayDefine({
  // 弹出层动画的持续时间, 可以避免组件过早被销毁
    duration: 1000,
  })

  return (
    <div className={visible && 'is--visible'}>
      <span onClick={() => resolve(`${props.title}:confirmed`)}> Confirm </span>
    </div>
  )
}

步骤.2: Create Overlay

你可以通过 defineOverlay 的方法将组件转换成模态框的方法,它允许你在 Javascript / Typescript 中调用。

ts
import { defineOverlay } from '@overlastic/react'
import { OverlayComponent } from './overlay'

// 转换为命令式回调
const callback = defineOverlay(OverlayComponent)
// 调用组件并获取 resolve 回调的值
const value = await callback({ title: 'callbackOverlay' })
// value === "callbackOverlay:confirmed"

你也可以通过 renderOverlay 直接调起组件,跳过 defineOverlay 方法。

ts
import { renderOverlay } from '@overlastic/react'
import { OverlayComponent } from './overlay'

const value = await renderOverlay(OverlayComponent, {
  title: 'useOverlayDefine'
})
// value === "useOverlayDefine:confirmed"

Injection Provider ✨ (v0.5.0)

如果有 Context、Provider 的情况下,使用 overlays 获取当前上下文都变得非常麻烦,而使用 useOverlayHolder 还需要将 holder 放到某个地方,而通过全局的 Provider 批量渲染所有弹窗,可以减少这部分的工作,我们提供了以下的组件与 Hooks 支持:

tsx
import { OverlayProvider } from '@overlastic/react'

function Main() {
  return (
    <OverlayProvider>
      <App />
    </OverlayProvider>
  )
}
export default Main

在页面中使用 Overlay Component:

tsx
import { useOverlay } from '@overlastic/react'
import CustomDialog from './Dialog.tsx'

function Page() {
  const openDialog = useOverlay(CustomDialog)
  async function onClick() {
    const resolved = await openDialog({ title: 'My Title' })
    console.log(resolved)
  }
  return (
    <button onClick={onClick}>
      Open Modal
    </button>
  )
}

export default Page

Injection Holder

除了使用 defineOverlayrenderOverlay 创建使用弹出层组件外,还支持使用 useOverlayHolder 创建在组件内部的弹出层组件,并继承应用的当前上下文。

tsx
import { useOverlayHolder } from '@overlastic/react'
import { OverlayComponent } from './overlay'

export function Main() {
  // 通过 useOverlayHolder(Component) 创建支持当前 context 的组件持有者
  const [holder, overlayApi] = useOverlayHolder(OverlayComponent)

  function open() {
  // 打开弹出层
    overlayApi()
      .then((result) => {})
  }
  return (
    <>
      <div onClick={open}> open </div>
      {/* 挂载 holder */}
      {holder}
    </>
  )
}

Define Component

使用 @overlastic/react 创建的组件,除了支持使用命令式(Imperative)方法调用外,还支持在 JSX 中使用。

这是一个可选项,在迁移旧组件时非常有用。

tsx
// 如果使用 Typescript 需要使用 PropsWithOverlays 定义 props 类型
import type { PropsWithOverlays } from '@overlastic/react'
import { useOverlayDefine } from '@overlastic/react'

export function OverlayComponent(props: PropsWithOverlays) {
  const { visible, resolve, reject } = useOverlayDefine({
    // 将 props 传递给 hooks 处理
    props
  })

  return (
    <div className={visible && 'is--visible'}>
      ...
    </div>
  )
}

组件接收 props 后,即可在 JSX 中使用弹出层组件。

tsx
import { useState } from 'react'
import { Component } from './overlay'

export function Main() {
  const [visible, setVisible] = useState(false)

  function openOverlay() {
    setVisible(true)
  }

  function onResolve(value) {
    setVisible(false)
  }

  function onReject(value) {
    setVisible(false)
  }

  return (
    <Component visible={visible} onResolve={onResolve} onReject={onReject} />
  )
}

如果您想替换为其他的字段与事件名,可以更改 eventsmodel 配置。

jsx
function Component(props: { onOn?: Function, onNook?: Function, open: boolean }) {
  const { visible, resolve, reject } = useOverlayDefine({
    events: { resolve: 'onOk', reject: 'onNook' },
    model: 'open',
    props,
  })
  // ...
}

Customized

您可以使用 @overlastic/react 修改现有组件或组件库,拿 antd(drawer) 举例:

tsx
import type { PropsWithOverlays } from '@overlastic/react'
import { useOverlayDefine } from '@overlastic/react'
import { Button, Drawer } from 'antd'

function MyDrawer(props: PropsWithOverlays<{ title: string }>) {
  const { visible, resolve, reject } = useOverlayDefine({
    duration: 200,
    props,
  })

  return (
    <Drawer title={props.title} onClose={reject} open={visible}>
      {/* Custom contents.... */}
      <Button type="primary" onClick={() => resolve(`${props.title}:confirmed`)}>
        Confirm
      </Button>
    </Drawer>
  )
}
export default MyDrawer

MIT Licensed