@edge-effect/react-abstract-dialog
TypeScript icon, indicating that this package has built-in type declarations

1.2.0 • Public • Published

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 하려고 할 때 문제가 발생 할 수 있습니다.

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

Package Sidebar

Install

npm i @edge-effect/react-abstract-dialog

Weekly Downloads

3

Version

1.2.0

License

MIT License

Unpacked Size

78 kB

Total Files

36

Last publish

Collaborators

  • dark1451