Skip to content

Latest commit

 

History

History
243 lines (187 loc) · 5.83 KB

README.md

File metadata and controls

243 lines (187 loc) · 5.83 KB

Overlay-manager-rc

English | 한국어

Inspired by angular cdk overlay

React overlay component manager

Feature

  • (alert-dialog, dialog, sheet...) open, close state no more management.
  • You don't need to worry about declaring overlay component. It's okay to have multiple overlays.
  • Delivering data to overlay component props.
  • Detect when an overlay component is closed; the resulting data is received on close.
  • Prevent closing with beforeClose logic. Asynchronous result handling with await.
  • Simplified API with automatic ID management.
  • No unnecessary renders when opening or closing overlay components.
  • React 19 support

Install

npm

npm install overlay-manager-rc

yarn

yarn add overlay-manager-rc

pnpm

pnpm add overlay-manager-rc

Setting

ex) nextjs(app router) + shadcn-ui(radix-ui)

already install

  • alert-dialog

Step1

make file overlay-manager-provider.tsx;

'use client';

import type { ReactNode } from 'react';
import { OverlayContainer } from "overlay-manager-rc";

export function OverlayContainerNext({ children }: { children?: ReactNode }) {
  return <OverlayContainer/>;
}

Step2

set provider in layout.tsx component

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body className={cn('min-h-screen font-sans antialiased dark')}>
        {children}
        <OverlayContainerNext />
      </body>
    </html>
  );
}

Usage

Create overlay component

import type {OverlayContentProps} from 'overlay-manager-rc';
import {useBeforeClose} from 'overlay-manager-rc'; // Import useBeforeClose

export function TestContent({
  open,
  data,
  close,
  id // add id prop
}: OverlayContentProps<string>) {

  return (
    <AlertDialog
      onOpenChange={(v) => {
        !v && close();
      }}
      open={open}
    >
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>Alert title</AlertDialogTitle>
          <AlertDialogDescription>Get Data: {data}</AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>Cancel</AlertDialogCancel>
          <AlertDialogAction>Continue</AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  );
}

Open overlay

'use client';

import { useOverlayManager } from 'overlay-manager-rc';

export function AlertSection() {
  const { openOverlay } = useOverlayManager();
  
  const handleOpenAlert = async () => {
    const result = await openOverlay({ 
      content: TestContent,
      data: 'hello!!!!',
      onClose: (result) => {
        console.log('Dialog closed with result:', result);
      },
      onOpen: (id) => {
        console.log('Overlay opened with id:', id);
      },
    });
    console.log('Result from openOverlay:', result); // Same value as onClose result
  };

  return (
    <section className="md:h-screen">
      <div className="flex flex-col gap-10">
        <Button onClick={handleOpenAlert}>
          show alert
        </Button>
      </div>
    </section>
  );
}

Manual ID Management

When you specify a manual ID and an overlay with the same ID is already open, the existing overlay will automatically close before opening the new one.

'use client';

import { useOverlayManager } from 'overlay-manager-rc';

export function AlertSection() {
  const { openOverlay } = useOverlayManager();
  
  const handleOpenAlert = async () => {
    // This will close any existing overlay with ID 'custom-alert' 
    // before opening the new one
    await openOverlay({ 
      id: 'custom-alert',
      content: TestContent,
      data: 'first alert!',
    });
  };

  const handleOpenAnotherAlert = async () => {
    // If 'custom-alert' is already open, it will close first
    await openOverlay({ 
      id: 'custom-alert',
      content: TestContent,
      data: 'second alert!',
    });
  };

  return (
    <section className="md:h-screen">
      <div className="flex flex-col gap-10">
        <Button onClick={handleOpenAlert}>First Alert</Button>
        <Button onClick={handleOpenAnotherAlert}>Second Alert</Button>
      </div>
    </section>
  );
}

API

useOverlayManager

returns

name description parameter
openOverlay Opens an overlay component. Returns a Promise. OverlayOptions
closeAllOverlays Closes all overlay components. -
closeOverlayById Closes an overlay component by ID. id: string

OverlayOptions<TData, TResult>

Prop Type Default Required
id string - No
content OverlayContent<TData, TResult> - Yes
data TData - No
onClose (result?: TResult) => void | Promise - No
onOpen (id: string) => void | Promise - No
beforeClose () => boolean | Promise - No

OverlayContentProps<TData, TResult>

Prop Type Default Required
data TData - Yes
close (result?: TResult) => void - Yes
open boolean - Yes
id string - Yes

useBeforeClose

When the manager tries to run an overlay with the same id function that executes before closing the overlay

import { useBeforeClose } from 'overlay-manager-rc/useBeforeClose';

// ... inside your overlay component
useBeforeClose(async () => {
  // Your logic to determine whether to prevent closing.
  // For example, check if a form is dirty.
  const canClose = window.confirm('Are you sure you want to close?');
  return canClose; // Return true to allow closing, false to prevent it.
}, id); // Pass the overlay's ID