Skip to content

@overlays/react

@overlays/react is used to define Overlay components in react and supports both imperative and declarative usage!

Install

With pnpm:

sh
pnpm add @overlays/react

With yarn:

sh
yarn add @overlays/react

Usage

Step 1: Define Component

tsx
// overlay.tsx
export function Component(props) {
  const { visible, resolve, reject } = useOverlay({
    // Duration of overlay duration, helps prevent premature component destruction
    duration: 200,
  })

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

Step 2: Create Overlay

You can convert the component into a modal dialog box by using the defineOverlay method, which allows you to call it in Javascript / Typescript.

ts
import { defineOverlay } from '@overlays/react'
import { Component } from './overlay'

// Convert to imperative callback
const callback = defineOverlay(Component)
// Call the component and get the resolve callback value
const value = await callback({ title: 'callbackOverlay' })
// value === "callbackOverlay:confirmed"

You can also directly call the component through renderOverlay, bypassing the defineOverlay method.

ts
import { renderOverlay } from '@overlays/react'
import { Component } from './overlay'

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

Injection Provider ✨ (v0.4.8)

In the case of using Provider, the overlays mode does not simply access the content injected into the current context. By supporting the following APIs, it allows the use of injected components to inherit the context:

tsx
import { OverlaysProvider } from '@overlays/react'

function Main() {
  return (
    <OverlaysProvider>
      <App />
    </OverlaysProvider>
  )
}

Using in a page:

tsx
import { useInjectProvider } from '@overlays/react'
import CustomDialog from './Dialog.tsx'

function Page() {
  const openDialog = useInjectProvider(CustomDialog)
  return (
    <button onClick={() => openDialog({ ... })}>
      Open Modal
    </button>
  )
}

Injection Holder

In addition to using defineOverlay and renderOverlay to create popup components, you can also use useInjectHolder to create popup components within a component and inherit the current context of the application.

tsx
import { useInjectHolder } from '@overlays/react'
import { OverlayComponent } from './overlay'

export function Main() {
  // Use useInjectHolder(Component) to create a component holder that supports the current context.
  const [holder, overlayApi] = useInjectHolder(OverlayComponent)

  function open() {
    // Open the popup component
    overlayApi()
      .then((result) => {})
  }
  return (<>
    <div onClick={open}> open </div>
    {/* Mount the holder */}
    {holder}
  </>)
}

Define Component

Components created using @overlays/react support both imperative and declarative methods of calling. In addition to imperative methods, these components can also be used in JSX.

This is an optional option that is very useful when porting old components.

tsx
// If using Typescript, use PropsWithOverlays to define props type
import type { PropsWithOverlays } from '@overlays/react'
import { useOverlay } from '@overlays/react'

export function Component(props: PropsWithOverlays<{ /* ...you props */ }>) {
  const { visible, resolve, reject } = useOverlay({
    // pass props to useOverlay for processing
    props
  })

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

Once the Overlay component has received props, the popup layer component can be used in 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} />
  )
}

If you want to replace other fields and event names, you can do so using the model and events config of useOverlay.

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

Customized

You can use @overlays/react to modify existing components or component libraries

Take antd(drawer) as an example:

tsx
import type { PropsWithOverlays } from '@overlays/react'
import { useOverlay } from '@overlays/react'
import { Button, Drawer } from 'antd'

function MyDrawer(props: PropsWithOverlays<{ title: string }>) {
  const { visible, resolve, reject } = useOverlay({
    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>
  )
}

MIT Licensed