react-flow-modal 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-flow-modal",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Promise-based modal flows for React",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,9 +23,6 @@
23
23
  "require": "./dist/index.cjs"
24
24
  }
25
25
  },
26
- "files": [
27
- "dist"
28
- ],
29
26
  "scripts": {
30
27
  "build": "tsup",
31
28
  "prepublishOnly": "pnpm build"
@@ -0,0 +1,12 @@
1
+ import React, { FC, Fragment } from 'react';
2
+ import { useModalContext } from './useModalContext';
3
+
4
+ export const ModalHost: FC = () => {
5
+ const { stack } = useModalContext();
6
+
7
+ return (
8
+ <Fragment>
9
+ {stack.map((item) => (item))}
10
+ </Fragment>
11
+ );
12
+ };
@@ -0,0 +1,12 @@
1
+ import React, { FC, PropsWithChildren, useState } from "react";
2
+ import { ModalContext } from "./modalContext";
3
+
4
+ export const ModalProvider: FC<PropsWithChildren> = ({ children }) => {
5
+ const [stack, setStack] = useState<React.ReactNode[]>([]);
6
+
7
+ return (
8
+ <ModalContext.Provider value={{ stack, setStack }}>
9
+ {children}
10
+ </ModalContext.Provider>
11
+ );
12
+ };
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { ModalProvider } from './ModalProvider';
2
+ export { ModalHost } from './ModalHost';
3
+ export { useModal } from './useModal';
@@ -0,0 +1,9 @@
1
+ import { createContext, Dispatch, SetStateAction } from "react";
2
+
3
+ export const ModalContext = createContext<{
4
+ stack: React.ReactNode[];
5
+ setStack: Dispatch<SetStateAction<React.ReactNode[]>>
6
+ }>({
7
+ stack: [],
8
+ setStack: () => [] as React.ReactNode[],
9
+ });
@@ -0,0 +1,37 @@
1
+ import React, { Fragment } from "react";
2
+ import { useModalContext } from "./useModalContext";
3
+
4
+ export const useModal = () => {
5
+ const { setStack } = useModalContext();
6
+
7
+ const pop = () => {
8
+ setStack((prev) => prev.slice(0, -1));
9
+ };
10
+
11
+ const push = (element: React.ReactNode) => {
12
+ setStack((prev) => [...prev, element]);
13
+ };
14
+
15
+ /**
16
+ * 모달을 열고 닫는 함수
17
+ * @caution 모달을 열고 닫는 함수는 비동기 함수이므로, 모달을 열고 닫는 함수를 호출하면, 반드시 resolve 또는 reject 함수를 호출해야 합니다.
18
+ * 그렇지 않으면 pending 상태가 되어 무한 대기 상태가 됩니다.
19
+ * @param key - 모달의 고유 키
20
+ * @param render - 모달을 렌더링하는 함수
21
+ * @returns - 모달 컴포넌트가 반환하는 값을 반환하는 Promise
22
+ */
23
+ const open = <T,>(key: string, render: (resolve: (value: T) => void, reject: (reason?: unknown) => void) => React.ReactNode) => {
24
+ return new Promise<T>((resolve, reject) => {
25
+ const element = render((value) => {
26
+ resolve(value);
27
+ pop();
28
+ }, (reason) => {
29
+ reject(reason);
30
+ pop();
31
+ });
32
+ push(<Fragment key={key}>{element}</Fragment>);
33
+ });
34
+ };
35
+
36
+ return { open };
37
+ };
@@ -0,0 +1,12 @@
1
+ import { useContext } from "react";
2
+ import { ModalContext } from "./modalContext";
3
+
4
+ export const useModalContext = () => {
5
+ const context = useContext(ModalContext);
6
+
7
+ if (!context) {
8
+ throw new Error('useModalContext must be used within a ModalProvider');
9
+ }
10
+
11
+ return context;
12
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2019",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+
7
+ "jsx": "react-jsx",
8
+
9
+ "declaration": true,
10
+ "declarationMap": true,
11
+ "emitDeclarationOnly": false,
12
+
13
+ "strict": true,
14
+ "skipLibCheck": true,
15
+
16
+ "esModuleInterop": true,
17
+ "forceConsistentCasingInFileNames": true,
18
+
19
+ "outDir": "dist",
20
+ "rootDir": "src"
21
+ },
22
+ "include": ["src"]
23
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["src/index.ts"],
5
+ format: ["esm", "cjs"],
6
+ dts: true,
7
+ clean: true,
8
+ });
package/README.md DELETED
@@ -1,100 +0,0 @@
1
- # react-flow-modal
2
-
3
- Promise-based modal flows for React.
4
-
5
- `react-flow-modal` lets you treat modals as async flows using
6
- `Promise` and `async/await`, without coupling your UI to state-driven logic.
7
-
8
- ---
9
-
10
- ## Installation
11
-
12
- ```bash
13
- pnpm add react-flow-modal
14
- # or
15
- npm install react-flow-modal
16
- # or
17
- yarn add react-flow-modal
18
- ```
19
-
20
- ## Basic Usage
21
-
22
- ```JSX
23
- import { ModalProvider, ModalHost, useModal } from "react-flow-modal";
24
-
25
- function App() {
26
- const modal = useModal();
27
-
28
- const onClick = async () => {
29
- const result = await modal.open("confirm",
30
- (resolve, reject) => (
31
- <ConfirmModal
32
- onConfirm={() => resolve(true)}
33
- onCancel={() => resolve(false)}
34
- />
35
- ));
36
-
37
- // Resolving or rejecting the promise will also remove the modal from the stack
38
-
39
- console.log(result);
40
- };
41
-
42
- return <button onClick={onClick}>Open modal</button>;
43
- }
44
-
45
- export default function Root() {
46
- return (
47
- <ModalProvider>
48
- <App />
49
- <ModalHost />
50
- </ModalProvider>
51
- );
52
- }
53
- ```
54
-
55
- ## API
56
-
57
- ### open
58
- ```TS
59
- open<T>(
60
- key: string,
61
- render: (
62
- resolve: (value: T) => void,
63
- reject: (reason?: unknown) => void
64
- ) => React.ReactNode
65
- ): Promise<T>
66
- ```
67
-
68
- ## Important
69
-
70
- > ⚠️ Make sure to always resolve or reject the promise.
71
- > Leaving it pending will block the async flow.
72
-
73
- ## Why react-flow-modal?
74
-
75
- Most modal libraries are state-driven:
76
- ```JSX
77
- setOpen(true);
78
- ```
79
-
80
- This makes modal control implicit and tightly coupled to rendering.
81
-
82
- react-flow-modal treats modals as explicit async control points:
83
-
84
- ```JSX
85
- const result = await open(...);
86
- ```
87
-
88
- This keeps control flow readable, composable, and testable.
89
-
90
- ## Features
91
-
92
- - Headless API (no styles, no UI constraints)
93
- - Promise-based modal control
94
- - Stack-based modal rendering
95
- - Fully controlled by user events
96
- - Works naturally with async / await
97
-
98
- ## License
99
-
100
- MIT