npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@edge-effect/react-abstract-dialog

v1.0.3

Published

implementing abstract dialog in react

Downloads

18

Readme

React abstract dialog

  • 해당 라이브러리는 리액트에서 동작하는 dialog 관리 context 입니다.
  • 쉽게 confirm, alert, toast 그리고 custom dialog 들을 해당 컨텍스트로 관리 할 수 있습니다.

Installation

$ npm install @edge-effect/react-abstract-dialog

Usage

모든 컴포넌트가 쓸 수 있도록 DialogContextProvider를 감쌉니다.

<DialogContextProvider>{/* your components */}</DialogContextProvider>

Props of DialogContextProvider

  • experimental_withHistory: 브라우저 뒤로/앞으로 등의 기록 처리를 실험적으로 설정합니다.
  • experimental_withHistoryForwardRestore: experimental_withHistory 가 활성화 됬을 때 실험적으로 히스토리 앞으로가기에 대해 dialog 를 복원하는 기능을 설정 합니다.
  • experimental_historySearchParamKey: 브라우저 기록 처리 시 사용되는 키 값을 실험적으로 정의합니다.
  • visibleMultipleDialog: 대화 상자를 스택 형태로 계속 노출할지 아니면 하나의 대화 상자만 노출할지 여부.
  • DialogContainer: 대화 상자를 래핑하는 컨테이너 구성 요소를 제공할 수 있습니다.
  • ToastContainer: 토스트를 포장하는 컨테이너 구성 요소를 제공할 수 있습니다.
  • Alert: 경고를 표시하는 구성요소를 제공합니다. 경고를 표시하려는 경우 필수입니다.
  • Confirm : 확인을 표시하는 컴포넌트를 제공합니다. 확인을 표시하려면 필수입니다.
  • Toast: 토스트를 표시하는 구성 요소를 제공합니다. 토스트를 표시하려는 경우 필수입니다.
  • onInterceptScrollBlocking: 대화상자가 표시될 때 적절한 작업을 처리할 수 있는 이벤트 리스너입니다.

경고, 확인, 알림을 컨텍스트에서 사용할 수 있도록 구성 요소를 제공해야 합니다.

import Confirm from "./component/popup/confirm";
import Alert from "./component/popup/alert";
import Toast from "./component/popup/toast";

<DialogContextProvider
    experimental_withHistory={true}
    ToastContainer={({ children }: { children: ReactNode }) => {
        return <div className="toast-container">{children}</div>;
    }}
    Confirm={Confirm}
    Alert={Alert}
    Toast={Toast}>
    {/* your components */}
</DialogContextProvider>;
  • 컨텍스트를 통해 confirm만 사용하는 경우, alerttoast를 제공할 필요가 없습니다.

useDialogContext를 사용하여 dialog를 제어합니다.

const MyComponent = () => {
    const { confirm } = useDialogContext();
    return (
        <>
            <button
                onClick={async () => {
                    if (await confirm({ title: "Hello", message: "Select your answer" })) {
                        console.log("result is true");
                    } else {
                        console.log("result is false");
                    }
                }}>
                Show confirm
            </button>
        </>
    );
};
  • DialogContextProvider에서 제공하는 confirm, alert, showDialog는 Promise를 리턴 합니다.
  • confirm은 Promise, alert은 Promise를 리턴합니다.
  • showDialog는 Promise 반환 값을 원하는 대로 설정할 수 있습니다.

Confirm, alert, and toast 는 어떤식으로 제공 해야 하나요?

  • useDialogContext 로부터 confirm, alert, toast 를 호출하면, DialogContextProvider 에 전달한 Confirm, Alert, Toast props 를 사용합니다.

  • 전달한 props 들은 컴포넌트로 아래와 같이 직접 구현해서 전달해야 하고, 각 각 AlertProps, ConfirmProps, ToastProps 를 사용하거나 상속 받아야 합니다.

  • 예를들어...

    import { AlertProps, useDialogActionContext } from "@edge-effect/react-abstract-dialog";
    
    const Alert = ({ title, message, textOk = "ok" }: AlertProps) => {
        const { hide } = useDialogActionContext();
        return (
            <>
                <section className="dialog">
                    <div
                        className="dialog-dimmed"
                        onClick={() => {
                            hide();
                        }}
                    />
                    <div className="dialog-content">
                        <h1>{title}</h1>
                        <h2>{message}</h2>
                        <ul className="horizontal">
                            <li>
                                <button
                                    onClick={() => {
                                        hide();
                                    }}>
                                    {textOk}
                                </button>
                            </li>
                        </ul>
                    </div>
                </section>
            </>
        );
    };
    
    export default Alert;
  • 위의 예시에서 DialogContextProvider 의해 관리될 dialog 는 useDialogActionContext 를 사용 해서 상호작용 해야 합니다. 해당 컨텍스트에서 hide 또는 hideAfter 등을 통해 dialog를 제어 해야 합니다.

  • hide 또는 hideAfter 를 호출하면서 dialog 가 dismiss 되어야만 promise 가 resolve 됩니다.

Customize dialog

  • 직접 dialog를 디자인하고 값을 호출부에 전달 할 수 있습니다.

  • 커스텀 dialog로 사용할 컴포넌트를 제작 합니다.

  • 예를들어

    type Choice = { id: number; display: string };
    
    export type MyCustomDialogResult = Array<Choice>;
    
    interface MyCustomDialogProps {
        choices: Array<Choice>;
    }
    
    const MyCustomDialog = ({ choices }: MyCustomDialogProps) => {
        const { hide } = useDialogActionContext<MyCustomDialogResult>();
    
        const checkedChoiceInfo = useRef<{ [id: string]: { isChecked: boolean; data: Choice } }>({});
    
        return (
            <>
                <section className="dialog">
                    <div
                        className="dialog-dimmed"
                        onClick={() => {
                            hide();
                        }}
                    />
                    <div className="dialog-content">
                        <h2>Select your choice</h2>
                        <ul>
                            {choices.map((choice) => {
                                const checkboxId = `checkboxId-${choice.id}-${choice.display}`;
                                return (
                                    <li key={choice.id}>
                                        <input
                                            type="checkbox"
                                            id={checkboxId}
                                            value={choice.id}
                                            onChange={(event) => {
                                                const isChecked = event.target.checked;
                                                if (checkedChoiceInfo.current[event.target.value] !== undefined) {
                                                    checkedChoiceInfo.current[event.target.value].isChecked = isChecked;
                                                } else {
                                                    checkedChoiceInfo.current[event.target.value] = {
                                                        isChecked: isChecked,
                                                        data: choice,
                                                    };
                                                }
                                            }}
                                        />
                                        <label htmlFor={checkboxId}>{choice.display}</label>
                                    </li>
                                );
                            })}
                        </ul>
                        <ul className="horizontal">
                            <li>
                                <button
                                    onClick={() => {
                                        hide();
                                    }}>
                                    Dismiss
                                </button>
                            </li>
                            <li>
                                <button
                                    onClick={() => {
                                        const selectedList: MyCustomDialogResult = [];
                                        const entries = Object.entries(checkedChoiceInfo.current);
                                        for (const [, value] of entries) {
                                            if (value.isChecked) {
                                                selectedList.push(value.data);
                                            }
                                        }
                                        hide(selectedList);
                                    }}>
                                    Select
                                </button>
                            </li>
                        </ul>
                    </div>
                </section>
            </>
        );
    };
    
    export default MyCustomDialog;
  • useDialogActionContext 에 데이터 유형을 전달 해서 hide 를 호출 할 때 값의 일관성을 갖도록 합니다. hideundefined 를 전달 할 수 있습니다.

  • 해당 dialog 를 표시하고자 하는 코드에서 아래와 같이 dialog를 제어 합니다.

    <button
        onClick={async () => {
            const dialog = await showDialog<MyCustomDialogResult>(<MyCustomDialog choices={choices} />);
            console.log("result", dialog.result);
        }}>
        Show custom dialog
    </button>
  • promise 는 {id, result} 를 리턴합니다. 해당 dialog 에서 전달 하는 값은 result 객체에 있습니다. 값을 사용하세요!

주의해주세요!

Dialog 에서 다른 페이지로 리디렉션

  • withHistory 기능을 활성화 하면, 브라우저 히스토리가 쌓이게 되고. 쌓인 상태로 dialog 에서 외부 또는 내부로 redirect 하려고 할 때 문제가 발생 할 수 있습니다.

    • 브라우저 히스토리가 쌓인 상태로 새로운 페이지로 리디렉션 한 뒤 다시 돌아오면 동작되지 않는 히스토리가 쌓여 문제가 됩니다.
    • useDialogActionContextdoNavigate 로 redirect 코드를 작성해야 합니다.
    • 예를들어
    // 이렇게 리디렉션 하면 히스토리 문제가 됩니다.
    navigate("/hello-world");
    // 대신 doNavigate 로 감싸야 합니다. doNavigate로 감싸면 리디렉션 전에 dialog와 관련된 브라우저 히스토리를 제거한 뒤 리디렉션 합니다.
    doNavigate(() => navigate("/hello-world"));
    • 자세한 내용은 dialog example 의 redirect 관련 내용을 참고하세요. (redirect-example-dialog.tsx)