@pop-kit/okcancel 0.0.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/README.md ADDED
@@ -0,0 +1,278 @@
1
+ # @pop-kit/okcancel
2
+
3
+ [![npm version](https://badge.fury.io/js/@pop-kit/okcancel.svg)](https://www.npmjs.com/package/@pop-kit/okcancel)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Live Demo](https://img.shields.io/badge/Live-Demo-blue?style=flat&logo=stackblitz)](https://stackblitz.com/edit/vitejs-vite-wszi61bn?file=src%2FApp.tsx)
6
+
7
+ **React + TypeScript 기반의 비동기 UI 인터랙션 라이브러리 (Headless & Logic-First)**입니다.
8
+ `confirm`, `alert` 뿐만 아니라 다양한 오버레이(Sheet, Toast 등)를 `await` 함수 호출로 제어할 수 있습니다.
9
+
10
+ ## ✨ 주요 특징
11
+
12
+ - **Promise 기반 로직** - `await confirm()` 한 줄로 복잡한 상태 관리 대체
13
+ - **Headless 아키텍처** - UI와 로직의 완전한 분리, Shadcn/MUI 등 어떤 스타일과도 호환
14
+ - **DX (개발자 경험) 중심** - 자동 로딩 상태 관리, TypeScript 지원
15
+ - **AI-Native** - Cursor, Copilot 등 AI가 코드를 작성하기 쉬운 구조
16
+
17
+ ## 📦 설치
18
+
19
+ ```bash
20
+ npm install @pop-kit/okcancel
21
+ ```
22
+
23
+ ## 🎮 Live Demo
24
+
25
+ [**StackBlitz에서 바로 체험하기**](https://stackblitz.com/edit/vitejs-vite-wszi61bn?file=src%2FApp.tsx)
26
+
27
+ ## 🚀 빠른 시작
28
+
29
+ ```tsx
30
+ import { OkCancelProvider, useOkCancel } from 'react-okcancel';
31
+
32
+ function MyComponent() {
33
+ const { confirm, alert } = useOkCancel();
34
+
35
+ const handleDelete = async () => {
36
+ const confirmed = await confirm({
37
+ title: '삭제 확인',
38
+ description: '정말로 이 항목을 삭제하시겠습니까?',
39
+ confirmText: '삭제',
40
+ cancelText: '취소',
41
+ });
42
+
43
+ if (confirmed) {
44
+ // 삭제 로직
45
+ await alert({
46
+ title: '완료',
47
+ description: '항목이 성공적으로 삭제되었습니다.',
48
+ });
49
+ }
50
+ };
51
+
52
+ return <button onClick={handleDelete}>항목 삭제</button>;
53
+ }
54
+
55
+ function App() {
56
+ return (
57
+ <OkCancelProvider>
58
+ <MyComponent />
59
+ </OkCancelProvider>
60
+ );
61
+ }
62
+ ```
63
+
64
+ ## 📖 함수 설명
65
+
66
+ ### `confirm(options: ConfirmOptions): Promise<boolean>`
67
+
68
+ 확인/취소 버튼이 있는 확인 다이얼로그를 표시합니다.
69
+
70
+ #### 옵션
71
+
72
+ | 옵션 | 타입 | 기본값 | 설명 |
73
+ | ------------------- | ----------- | -------- | ------------------------------------- |
74
+ | `title` | `ReactNode` | - | 다이얼로그 제목 |
75
+ | `description` | `ReactNode` | - | 다이얼로그 내용/메시지 |
76
+ | `confirmText` | `ReactNode` | `'확인'` | 확인 버튼 텍스트 |
77
+ | `cancelText` | `ReactNode` | `'취소'` | 취소 버튼 텍스트 |
78
+ | `canCloseOnOverlay` | `boolean` | `true` | 오버레이 클릭으로 닫기 허용 |
79
+ | `canCloseOnEsc` | `boolean` | `true` | Escape 키로 닫기 허용 |
80
+ | `showCloseButton` | `boolean` | `false` | 오른쪽 상단 X 버튼 표시 |
81
+ | `enableAnimation` | `boolean` | `true` | 다이얼로그 슬라이드 애니메이션 활성화 |
82
+
83
+ #### 반환값
84
+
85
+ - `true` 사용자가 확인 버튼을 클릭한 경우
86
+ - `false` 사용자가 취소 버튼을 클릭하거나, Escape를 누르거나, 오버레이를 클릭한 경우
87
+
88
+ #### 예제
89
+
90
+ ```tsx
91
+ const result = await confirm({
92
+ title: '저장 확인',
93
+ description: (
94
+ <div>
95
+ <p>변경사항을 저장하시겠습니까?</p>
96
+ <p>
97
+ <strong>주의:</strong> 이 작업은 되돌릴 수 없습니다.
98
+ </p>
99
+ </div>
100
+ ),
101
+ confirmText: '저장',
102
+ cancelText: '취소',
103
+ canCloseOnOverlay: false, // 실수로 닫히는 것 방지
104
+ showCloseButton: true, // X 버튼 표시
105
+ enableAnimation: false, // 애니메이션 비활성화
106
+ });
107
+
108
+ if (result) {
109
+ console.log('사용자가 저장을 확인했습니다');
110
+ } else {
111
+ console.log('사용자가 저장을 취소했습니다');
112
+ }
113
+ ```
114
+
115
+ ### `alert(options: AlertOptions): Promise<void>`
116
+
117
+ 확인 버튼만 있는 알림 다이얼로그를 표시합니다.
118
+
119
+ #### 옵션
120
+
121
+ | 옵션 | 타입 | 기본값 | 설명 |
122
+ | ------------------- | ----------- | -------- | ------------------------------------- |
123
+ | `title` | `ReactNode` | - | 다이얼로그 제목 |
124
+ | `description` | `ReactNode` | - | 다이얼로그 내용/메시지 |
125
+ | `confirmText` | `ReactNode` | `'확인'` | 확인 버튼 텍스트 |
126
+ | `canCloseOnOverlay` | `boolean` | `true` | 오버레이 클릭으로 닫기 허용 |
127
+ | `canCloseOnEsc` | `boolean` | `true` | Escape 키로 닫기 허용 |
128
+ | `showCloseButton` | `boolean` | `false` | 오른쪽 상단 X 버튼 표시 |
129
+ | `enableAnimation` | `boolean` | `true` | 다이얼로그 슬라이드 애니메이션 활성화 |
130
+
131
+ #### 반환값
132
+
133
+ - `undefined` 다이얼로그가 닫힐 때 (닫힌 방법과 관계없이)
134
+
135
+ #### 예제
136
+
137
+ ```tsx
138
+ await alert({
139
+ title: '성공',
140
+ description: '파일이 성공적으로 업로드되었습니다.',
141
+ confirmText: '확인',
142
+ });
143
+
144
+ console.log('사용자가 알림을 확인했습니다');
145
+ ```
146
+
147
+ ## 🎨 커스터마이징
148
+
149
+ CSS 변수를 재정의하여 모양을 커스터마이징할 수 있습니다:
150
+
151
+ ```css
152
+ :root {
153
+ /* 다이얼로그 */
154
+ --okcancel-dialog-bg: #fff;
155
+ --okcancel-dialog-border-radius: 8px;
156
+ --okcancel-dialog-shadow: 0 20px 25px -5px rgb(0 0 0 / 10%), 0 8px 10px -6px rgb(0 0 0 / 10%);
157
+ --okcancel-dialog-max-width: none;
158
+ --okcancel-dialog-min-width: auto;
159
+ --okcancel-dialog-max-height: none;
160
+ --okcancel-dialog-min-height: auto;
161
+ --okcancel-dialog-width: 500px;
162
+ --okcancel-dialog-height: max-content;
163
+ --okcancel-dialog-padding: 16px 24px;
164
+ --okcancel-dialog-border: none;
165
+
166
+ /* 다이얼로그 콘텐츠 */
167
+ --okcancel-content-padding: 0;
168
+ --okcancel-content-gap: 8px;
169
+ --okcancel-content-flex-direction: column;
170
+ --okcancel-content-justify-content: center;
171
+ --okcancel-content-align-items: normal;
172
+ --okcancel-content-height: auto;
173
+ --okcancel-content-max-height: none;
174
+ --okcancel-content-min-height: 100px;
175
+
176
+ /* 제목 */
177
+ --okcancel-title-font-size: 20px;
178
+ --okcancel-title-color: #222;
179
+ --okcancel-title-font-weight: 600;
180
+
181
+ /* 설명 */
182
+ --okcancel-description-font-size: 16px;
183
+ --okcancel-description-color: #8e8e8e;
184
+ --okcancel-description-font-weight: 400;
185
+
186
+ /* 버튼 컨테이너 */
187
+ --okcancel-btn-box-padding: 10px 0;
188
+ --okcancel-btn-box-gap: 12px;
189
+ --okcancel-btn-box-flex-direction: row;
190
+ --okcancel-btn-box-justify-content: flex-end;
191
+ --okcancel-btn-box-align-items: center;
192
+ --okcancel-btn-box-height: auto;
193
+
194
+ /* 버튼 */
195
+ --okcancel-button-padding: 8px 16px;
196
+ --okcancel-button-border-radius: 6px;
197
+ --okcancel-button-width: 100px;
198
+ --okcancel-button-height: 40px;
199
+ --okcancel-button-max-width: none;
200
+ --okcancel-button-min-width: auto;
201
+ --okcancel-button-border: none;
202
+ --okcancel-button-box-shadow: none;
203
+ --okcancel-button-font-size: 16px;
204
+ --okcancel-button-font-weight: 500;
205
+ --okcancel-button-text-align: center;
206
+ --okcancel-button-flex: 0 1 auto;
207
+
208
+ /* 주 버튼 (확인) */
209
+ --okcancel-button-primary-bg: #2c89e5;
210
+ --okcancel-button-primary-color: #fff;
211
+ --okcancel-button-primary-border: none;
212
+
213
+ /* 보조 버튼 (취소) */
214
+ --okcancel-button-secondary-bg: transparent;
215
+ --okcancel-button-secondary-color: #222;
216
+ --okcancel-button-secondary-border: 1px solid #d4d4d4;
217
+
218
+ /* 오버레이 */
219
+ --okcancel-overlay-bg: rgb(0 0 0 / 50%);
220
+ --okcancel-z-index: 1000;
221
+ }
222
+ ```
223
+
224
+ ### 반응형 커스터마이징
225
+
226
+ 모바일에서는 자동으로 다음과 같이 조정됩니다:
227
+
228
+ - 다이얼로그 너비: 화면의 85%
229
+ - 버튼 간격: 더 넓게 조정
230
+ - 버튼: 전체 너비로 확장
231
+
232
+ 모바일 스타일을 커스터마이징하려면:
233
+
234
+ ```css
235
+ @media (width < 640px) {
236
+ :root {
237
+ --okcancel-dialog-width: 90%; /* 모바일 너비 조정 */
238
+ --okcancel-btn-box-gap: 16px; /* 버튼 간격 조정 */
239
+ --okcancel-button-flex: 1; /* 버튼 전체 너비 */
240
+ --okcancel-btn-box-flex-direction: column; /* 버튼 세로 배치 */
241
+ }
242
+ }
243
+ ```
244
+
245
+ ## ♿ 접근성 기능
246
+
247
+ - **ARIA 역할**: 적절한 `dialog` 및 `alertdialog` 역할
248
+ - **포커스 관리**: 확인 버튼에 자동 포커스, 포커스 트래핑
249
+ - **키보드 내비게이션**:
250
+ - `Tab` / `Shift+Tab`으로 버튼 간 이동
251
+ - `Enter` / `Space`로 포커스된 버튼 활성화
252
+ - `Escape`로 다이얼로그 닫기 (활성화된 경우)
253
+ - **스크린 리더 지원**: 적절한 라벨링 및 알림
254
+
255
+ ## 🔧 고급 사용법
256
+
257
+ ### JSX를 사용한 커스텀 콘텐츠
258
+
259
+ ```tsx
260
+ const result = await confirm({
261
+ title: '파일 삭제',
262
+ description: (
263
+ <div>
264
+ <p>다음 파일들을 삭제하시겠습니까?</p>
265
+ <ul>
266
+ <li>document.pdf</li>
267
+ <li>image.jpg</li>
268
+ <li>data.json</li>
269
+ </ul>
270
+ <p>
271
+ <strong>경고:</strong> 이 작업은 되돌릴 수 없습니다.
272
+ </p>
273
+ </div>
274
+ ),
275
+ confirmText: '삭제',
276
+ cancelText: '취소',
277
+ });
278
+ ```
@@ -0,0 +1,499 @@
1
+ (function(){"use strict";try{if(typeof document<"u"){var t=document.createElement("style");t.appendChild(document.createTextNode("._overlay_ht92t_1{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;z-index:var(--okcancel-z-index);background-color:var(--okcancel-overlay-bg)}._overlay_ht92t_1[data-phase=enter]{animation:_okcancel-fade-in_ht92t_1 .4s cubic-bezier(.175,.885,.32,1.275) forwards}._overlay_ht92t_1[data-phase=exit]{animation:_okcancel-fade-out_ht92t_1 .4s cubic-bezier(.68,-.55,.265,1.55) forwards}._dialog_ht92t_17{background:var(--okcancel-dialog-bg);border-radius:var(--okcancel-dialog-border-radius);box-shadow:var(--okcancel-dialog-shadow);max-width:var(--okcancel-dialog-max-width);min-width:var(--okcancel-dialog-min-width);max-height:var(--okcancel-dialog-max-height);min-height:var(--okcancel-dialog-min-height);width:var(--okcancel-dialog-width);height:var(--okcancel-dialog-height);border:var(--okcancel-dialog-border);padding:var(--okcancel-dialog-padding)}._dialog_ht92t_17[data-animation=true][data-phase=enter]{animation:_okcancel-slide-in_ht92t_1 .4s cubic-bezier(.175,.885,.32,1.275) forwards}._dialog_ht92t_17[data-animation=true][data-phase=exit]{animation:_okcancel-slide-out_ht92t_1 .4s cubic-bezier(.68,-.55,.265,1.55) forwards}._dialog_ht92t_17[data-animation=false]{transform:translateY(0)}._dialog_ht92t_17 ._content_ht92t_39{display:flex;flex-direction:var(--okcancel-content-flex-direction);justify-content:var(--okcancel-content-justify-content);align-items:var(--okcancel-content-align-items);gap:var(--okcancel-content-gap);padding:var(--okcancel-content-padding);height:var(--okcancel-content-height);max-height:var(--okcancel-content-max-height);min-height:var(--okcancel-content-min-height)}._dialog_ht92t_17 ._content_ht92t_39 ._title_ht92t_50{font-size:var(--okcancel-title-font-size);font-weight:var(--okcancel-title-font-weight);color:var(--okcancel-title-color)}._dialog_ht92t_17 ._content_ht92t_39 ._description_ht92t_55{font-size:var(--okcancel-description-font-size);font-weight:var(--okcancel-description-font-weight);color:var(--okcancel-description-color)}._dialog_ht92t_17 ._close-button_ht92t_60{position:absolute;top:8px;right:8px;background:transparent;border:none;font-size:20px;cursor:pointer;width:32px;height:32px;display:flex;align-items:center;justify-content:center;color:#000}._dialog_ht92t_17 ._btn-box_ht92t_75{display:flex;flex-direction:var(--okcancel-btn-box-flex-direction);justify-content:var(--okcancel-btn-box-justify-content);align-items:var(--okcancel-btn-box-align-items);gap:var(--okcancel-btn-box-gap);padding:var(--okcancel-btn-box-padding);height:var(--okcancel-btn-box-height)}._dialog_ht92t_17 ._btn-box_ht92t_75 button{border:var(--okcancel-button-border);border-radius:var(--okcancel-button-border-radius);box-shadow:var(--okcancel-button-box-shadow);padding:var(--okcancel-button-padding);font-size:var(--okcancel-button-font-size);font-weight:var(--okcancel-button-font-weight);text-align:var(--okcancel-button-text-align);cursor:pointer;min-width:var(--okcancel-button-min-width);max-width:var(--okcancel-button-max-width);width:var(--okcancel-button-width);height:var(--okcancel-button-height);flex:var(--okcancel-button-flex)}._dialog_ht92t_17 ._btn-box_ht92t_75 button._button-primary_ht92t_99{background-color:var(--okcancel-button-primary-bg);color:var(--okcancel-button-primary-color);border:var(--okcancel-button-primary-border)}._dialog_ht92t_17 ._btn-box_ht92t_75 button._button-secondary_ht92t_104{background-color:var(--okcancel-button-secondary-bg);color:var(--okcancel-button-secondary-color);border:var(--okcancel-button-secondary-border)}@keyframes _okcancel-fade-in_ht92t_1{0%{opacity:0}to{opacity:1}}@keyframes _okcancel-fade-out_ht92t_1{0%{opacity:1}to{opacity:0}}@keyframes _okcancel-slide-in_ht92t_1{0%{transform:translateY(50vh)}to{transform:translateY(0)}}@keyframes _okcancel-slide-out_ht92t_1{0%{transform:translateY(0)}to{transform:translateY(50vh)}}:root{--okcancel-dialog-bg: #fff;--okcancel-dialog-border-radius: 8px;--okcancel-dialog-shadow: 0 20px 25px -5px rgb(0 0 0 / 10%), 0 8px 10px -6px rgb(0 0 0 / 10%);--okcancel-dialog-max-width: none;--okcancel-dialog-min-width: auto;--okcancel-dialog-max-height: none;--okcancel-dialog-min-height: auto;--okcancel-dialog-width: 500px;--okcancel-dialog-height: max-content;--okcancel-dialog-padding: 16px 24px;--okcancel-dialog-border: none;--okcancel-content-padding: 0;--okcancel-content-gap: 8px;--okcancel-content-flex-direction: column;--okcancel-content-justify-content: center;--okcancel-content-align-items: normal;--okcancel-content-height: auto;--okcancel-content-max-height: none;--okcancel-content-min-height: 100px;--okcancel-title-font-size: 20px;--okcancel-title-color: #222;--okcancel-title-font-weight: 600;--okcancel-description-font-size: 16px;--okcancel-description-color: #8e8e8e;--okcancel-description-font-weight: 400;--okcancel-btn-box-padding: 10px 0;--okcancel-btn-box-gap: 12px;--okcancel-btn-box-flex-direction: row;--okcancel-btn-box-justify-content: flex-end;--okcancel-btn-box-align-items: center;--okcancel-btn-box-height: auto;--okcancel-button-padding: 8px 16px;--okcancel-button-border-radius: 6px;--okcancel-button-width: 100px;--okcancel-button-height: 40px;--okcancel-button-max-width: none;--okcancel-button-min-width: auto;--okcancel-button-border: none;--okcancel-button-box-shadow: none;--okcancel-button-font-size: 16px;--okcancel-button-font-weight: 500;--okcancel-button-text-align: center;--okcancel-button-flex: 0 1 auto;--okcancel-button-primary-bg: #2c89e5;--okcancel-button-primary-color: #fff;--okcancel-button-primary-border: none;--okcancel-button-secondary-bg: transparent;--okcancel-button-secondary-color: #222;--okcancel-button-secondary-border: 1px solid #d4d4d4;--okcancel-overlay-bg: rgb(0 0 0 / 50%);--okcancel-z-index: 1000}@media (width < 640px){:root{--okcancel-dialog-width: 85%;--okcancel-btn-box-gap: 20px;--okcancel-button-flex: 1}}#okcancel-portal-root{position:relative;z-index:var(--okcancel-z-index)}")),document.head.appendChild(t)}}catch(o){console.error("vite-plugin-css-injected-by-js",o)}})();
2
+ import ie, { createContext as le, useContext as ue, useState as A, useEffect as H, useCallback as F } from "react";
3
+ import { createPortal as de } from "react-dom";
4
+ var S = { exports: {} }, j = {};
5
+ /**
6
+ * @license React
7
+ * react-jsx-runtime.production.js
8
+ *
9
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
10
+ *
11
+ * This source code is licensed under the MIT license found in the
12
+ * LICENSE file in the root directory of this source tree.
13
+ */
14
+ var V;
15
+ function fe() {
16
+ if (V) return j;
17
+ V = 1;
18
+ var n = Symbol.for("react.transitional.element"), t = Symbol.for("react.fragment");
19
+ function o(a, c, d) {
20
+ var m = null;
21
+ if (d !== void 0 && (m = "" + d), c.key !== void 0 && (m = "" + c.key), "key" in c) {
22
+ d = {};
23
+ for (var h in c)
24
+ h !== "key" && (d[h] = c[h]);
25
+ } else d = c;
26
+ return c = d.ref, {
27
+ $$typeof: n,
28
+ type: a,
29
+ key: m,
30
+ ref: c !== void 0 ? c : null,
31
+ props: d
32
+ };
33
+ }
34
+ return j.Fragment = t, j.jsx = o, j.jsxs = o, j;
35
+ }
36
+ var P = {};
37
+ /**
38
+ * @license React
39
+ * react-jsx-runtime.development.js
40
+ *
41
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
42
+ *
43
+ * This source code is licensed under the MIT license found in the
44
+ * LICENSE file in the root directory of this source tree.
45
+ */
46
+ var G;
47
+ function me() {
48
+ return G || (G = 1, process.env.NODE_ENV !== "production" && (function() {
49
+ function n(e) {
50
+ if (e == null) return null;
51
+ if (typeof e == "function")
52
+ return e.$$typeof === se ? null : e.displayName || e.name || null;
53
+ if (typeof e == "string") return e;
54
+ switch (e) {
55
+ case T:
56
+ return "Fragment";
57
+ case f:
58
+ return "Profiler";
59
+ case N:
60
+ return "StrictMode";
61
+ case te:
62
+ return "Suspense";
63
+ case re:
64
+ return "SuspenseList";
65
+ case oe:
66
+ return "Activity";
67
+ }
68
+ if (typeof e == "object")
69
+ switch (typeof e.tag == "number" && console.error(
70
+ "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
71
+ ), e.$$typeof) {
72
+ case v:
73
+ return "Portal";
74
+ case K:
75
+ return (e.displayName || "Context") + ".Provider";
76
+ case O:
77
+ return (e._context.displayName || "Context") + ".Consumer";
78
+ case ee:
79
+ var r = e.render;
80
+ return e = e.displayName, e || (e = r.displayName || r.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
81
+ case ne:
82
+ return r = e.displayName || null, r !== null ? r : n(e.type) || "Memo";
83
+ case M:
84
+ r = e._payload, e = e._init;
85
+ try {
86
+ return n(e(r));
87
+ } catch {
88
+ }
89
+ }
90
+ return null;
91
+ }
92
+ function t(e) {
93
+ return "" + e;
94
+ }
95
+ function o(e) {
96
+ try {
97
+ t(e);
98
+ var r = !1;
99
+ } catch {
100
+ r = !0;
101
+ }
102
+ if (r) {
103
+ r = console;
104
+ var s = r.error, l = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
105
+ return s.call(
106
+ r,
107
+ "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
108
+ l
109
+ ), t(e);
110
+ }
111
+ }
112
+ function a(e) {
113
+ if (e === T) return "<>";
114
+ if (typeof e == "object" && e !== null && e.$$typeof === M)
115
+ return "<...>";
116
+ try {
117
+ var r = n(e);
118
+ return r ? "<" + r + ">" : "<...>";
119
+ } catch {
120
+ return "<...>";
121
+ }
122
+ }
123
+ function c() {
124
+ var e = C.A;
125
+ return e === null ? null : e.getOwner();
126
+ }
127
+ function d() {
128
+ return Error("react-stack-top-frame");
129
+ }
130
+ function m(e) {
131
+ if ($.call(e, "key")) {
132
+ var r = Object.getOwnPropertyDescriptor(e, "key").get;
133
+ if (r && r.isReactWarning) return !1;
134
+ }
135
+ return e.key !== void 0;
136
+ }
137
+ function h(e, r) {
138
+ function s() {
139
+ W || (W = !0, console.error(
140
+ "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
141
+ r
142
+ ));
143
+ }
144
+ s.isReactWarning = !0, Object.defineProperty(e, "key", {
145
+ get: s,
146
+ configurable: !0
147
+ });
148
+ }
149
+ function _() {
150
+ var e = n(this.type);
151
+ return U[e] || (U[e] = !0, console.error(
152
+ "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
153
+ )), e = this.props.ref, e !== void 0 ? e : null;
154
+ }
155
+ function g(e, r, s, l, E, p, Y, D) {
156
+ return s = p.ref, e = {
157
+ $$typeof: w,
158
+ type: e,
159
+ key: r,
160
+ props: p,
161
+ _owner: E
162
+ }, (s !== void 0 ? s : null) !== null ? Object.defineProperty(e, "ref", {
163
+ enumerable: !1,
164
+ get: _
165
+ }) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
166
+ configurable: !1,
167
+ enumerable: !1,
168
+ writable: !0,
169
+ value: 0
170
+ }), Object.defineProperty(e, "_debugInfo", {
171
+ configurable: !1,
172
+ enumerable: !1,
173
+ writable: !0,
174
+ value: null
175
+ }), Object.defineProperty(e, "_debugStack", {
176
+ configurable: !1,
177
+ enumerable: !1,
178
+ writable: !0,
179
+ value: Y
180
+ }), Object.defineProperty(e, "_debugTask", {
181
+ configurable: !1,
182
+ enumerable: !1,
183
+ writable: !0,
184
+ value: D
185
+ }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
186
+ }
187
+ function k(e, r, s, l, E, p, Y, D) {
188
+ var u = r.children;
189
+ if (u !== void 0)
190
+ if (l)
191
+ if (ae(u)) {
192
+ for (l = 0; l < u.length; l++)
193
+ y(u[l]);
194
+ Object.freeze && Object.freeze(u);
195
+ } else
196
+ console.error(
197
+ "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
198
+ );
199
+ else y(u);
200
+ if ($.call(r, "key")) {
201
+ u = n(e);
202
+ var x = Object.keys(r).filter(function(ce) {
203
+ return ce !== "key";
204
+ });
205
+ l = 0 < x.length ? "{key: someKey, " + x.join(": ..., ") + ": ...}" : "{key: someKey}", z[u + l] || (x = 0 < x.length ? "{" + x.join(": ..., ") + ": ...}" : "{}", console.error(
206
+ `A props object containing a "key" prop is being spread into JSX:
207
+ let props = %s;
208
+ <%s {...props} />
209
+ React keys must be passed directly to JSX without using spread:
210
+ let props = %s;
211
+ <%s key={someKey} {...props} />`,
212
+ l,
213
+ u,
214
+ x,
215
+ u
216
+ ), z[u + l] = !0);
217
+ }
218
+ if (u = null, s !== void 0 && (o(s), u = "" + s), m(r) && (o(r.key), u = "" + r.key), "key" in r) {
219
+ s = {};
220
+ for (var L in r)
221
+ L !== "key" && (s[L] = r[L]);
222
+ } else s = r;
223
+ return u && h(
224
+ s,
225
+ typeof e == "function" ? e.displayName || e.name || "Unknown" : e
226
+ ), g(
227
+ e,
228
+ u,
229
+ p,
230
+ E,
231
+ c(),
232
+ s,
233
+ Y,
234
+ D
235
+ );
236
+ }
237
+ function y(e) {
238
+ typeof e == "object" && e !== null && e.$$typeof === w && e._store && (e._store.validated = 1);
239
+ }
240
+ var R = ie, w = Symbol.for("react.transitional.element"), v = Symbol.for("react.portal"), T = Symbol.for("react.fragment"), N = Symbol.for("react.strict_mode"), f = Symbol.for("react.profiler"), O = Symbol.for("react.consumer"), K = Symbol.for("react.context"), ee = Symbol.for("react.forward_ref"), te = Symbol.for("react.suspense"), re = Symbol.for("react.suspense_list"), ne = Symbol.for("react.memo"), M = Symbol.for("react.lazy"), oe = Symbol.for("react.activity"), se = Symbol.for("react.client.reference"), C = R.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, $ = Object.prototype.hasOwnProperty, ae = Array.isArray, I = console.createTask ? console.createTask : function() {
241
+ return null;
242
+ };
243
+ R = {
244
+ react_stack_bottom_frame: function(e) {
245
+ return e();
246
+ }
247
+ };
248
+ var W, U = {}, q = R.react_stack_bottom_frame.bind(
249
+ R,
250
+ d
251
+ )(), J = I(a(d)), z = {};
252
+ P.Fragment = T, P.jsx = function(e, r, s, l, E) {
253
+ var p = 1e4 > C.recentlyCreatedOwnerStacks++;
254
+ return k(
255
+ e,
256
+ r,
257
+ s,
258
+ !1,
259
+ l,
260
+ E,
261
+ p ? Error("react-stack-top-frame") : q,
262
+ p ? I(a(e)) : J
263
+ );
264
+ }, P.jsxs = function(e, r, s, l, E) {
265
+ var p = 1e4 > C.recentlyCreatedOwnerStacks++;
266
+ return k(
267
+ e,
268
+ r,
269
+ s,
270
+ !0,
271
+ l,
272
+ E,
273
+ p ? Error("react-stack-top-frame") : q,
274
+ p ? I(a(e)) : J
275
+ );
276
+ };
277
+ })()), P;
278
+ }
279
+ var X;
280
+ function _e() {
281
+ return X || (X = 1, process.env.NODE_ENV === "production" ? S.exports = fe() : S.exports = me()), S.exports;
282
+ }
283
+ var i = _e();
284
+ const Z = le(null), pe = () => Math.random().toString(36).substring(2, 9);
285
+ class he {
286
+ _instances = [];
287
+ _listeners = /* @__PURE__ */ new Set();
288
+ push(t, o, a) {
289
+ return new Promise((c, d) => {
290
+ const m = a?.id || pe(), h = {
291
+ id: m,
292
+ component: t,
293
+ props: o || {},
294
+ resolve: (_) => {
295
+ this.removeInstance(m), c(_);
296
+ },
297
+ reject: (_) => {
298
+ this.removeInstance(m), d(_);
299
+ },
300
+ createdAt: Date.now()
301
+ };
302
+ this._instances = [...this._instances, h], this.notify();
303
+ });
304
+ }
305
+ pop(t) {
306
+ if (this._instances.length !== 0)
307
+ if (t)
308
+ this.reject(t, new Error("Popup closed explicitly"));
309
+ else {
310
+ const o = this._instances[this._instances.length - 1];
311
+ this.reject(o.id, new Error("Popup closed explicitly"));
312
+ }
313
+ }
314
+ resolve(t, o) {
315
+ const a = this._instances.find((c) => c.id === t);
316
+ a && a.resolve(o);
317
+ }
318
+ reject(t, o) {
319
+ const a = this._instances.find((c) => c.id === t);
320
+ a && a.reject(o);
321
+ }
322
+ getInstances() {
323
+ return this._instances;
324
+ }
325
+ subscribe(t) {
326
+ return this._listeners.add(t), t(this._instances), () => {
327
+ this._listeners.delete(t);
328
+ };
329
+ }
330
+ removeInstance(t) {
331
+ this._instances = this._instances.filter((o) => o.id !== t), this.notify();
332
+ }
333
+ notify() {
334
+ this._listeners.forEach((t) => t(this._instances));
335
+ }
336
+ }
337
+ const be = () => new he(), Q = () => {
338
+ const n = ue(Z);
339
+ if (!n)
340
+ throw new Error("usePop must be used within a PopProvider");
341
+ return n;
342
+ }, ve = () => {
343
+ const n = Q(), [t, o] = A(n.getInstances());
344
+ return H(() => n.subscribe(o), [n]), t;
345
+ }, Ee = () => {
346
+ const n = ve();
347
+ return n.length === 0 ? null : /* @__PURE__ */ i.jsx(i.Fragment, { children: n.map((t) => {
348
+ const o = t.component;
349
+ return /* @__PURE__ */ i.jsx(o, { ...t.props }, t.id);
350
+ }) });
351
+ }, Re = ({ children: n }) => {
352
+ const [t] = A(() => be());
353
+ return /* @__PURE__ */ i.jsxs(Z.Provider, { value: t, children: [
354
+ n,
355
+ /* @__PURE__ */ i.jsx(Ee, {})
356
+ ] });
357
+ };
358
+ function Oe({ children: n }) {
359
+ return /* @__PURE__ */ i.jsx(Re, { children: n });
360
+ }
361
+ function ke({ children: n }) {
362
+ let t = document.getElementById("okcancel-portal-root");
363
+ return t || (t = document.createElement("div"), t.id = "okcancel-portal-root", document.body.appendChild(t)), de(n, t);
364
+ }
365
+ const xe = "_overlay_ht92t_1", ge = "_dialog_ht92t_17", Te = "_content_ht92t_39", je = "_title_ht92t_50", Pe = "_description_ht92t_55", b = {
366
+ overlay: xe,
367
+ dialog: ge,
368
+ content: Te,
369
+ title: je,
370
+ description: Pe,
371
+ "close-button": "_close-button_ht92t_60",
372
+ "btn-box": "_btn-box_ht92t_75",
373
+ "button-primary": "_button-primary_ht92t_99",
374
+ "button-secondary": "_button-secondary_ht92t_104"
375
+ };
376
+ function B({
377
+ type: n,
378
+ title: t,
379
+ description: o,
380
+ confirmText: a,
381
+ cancelText: c,
382
+ canCloseOnOverlay: d = !0,
383
+ canCloseOnEsc: m = !0,
384
+ showCloseButton: h = !1,
385
+ enableAnimation: _ = !0,
386
+ resolve: g
387
+ }) {
388
+ const [k, y] = A("enter"), [R, w] = A(null), v = F(
389
+ (f) => {
390
+ if (!_) {
391
+ g(n === "confirm" ? f : void 0);
392
+ return;
393
+ }
394
+ w(f), y("exit");
395
+ },
396
+ [_, g, n]
397
+ ), T = (f) => {
398
+ f.target === f.currentTarget && d && v(!1);
399
+ }, N = (f) => {
400
+ _ && f.target === f.currentTarget && k === "exit" && R !== null && g(n === "confirm" ? R : void 0);
401
+ };
402
+ return H(() => {
403
+ const f = (O) => {
404
+ O.key === "Escape" && m && (O.preventDefault(), v(!1));
405
+ };
406
+ return document.addEventListener("keydown", f), () => document.removeEventListener("keydown", f);
407
+ }, [m, v]), /* @__PURE__ */ i.jsx(ke, { children: /* @__PURE__ */ i.jsx("div", { className: b.overlay, "data-phase": k, onClick: T, children: /* @__PURE__ */ i.jsxs(
408
+ "dialog",
409
+ {
410
+ className: b.dialog,
411
+ "data-phase": k,
412
+ "data-animation": _ ?? !0,
413
+ "aria-labelledby": t ? "okcancel-title" : void 0,
414
+ "aria-describedby": o ? "okcancel-description" : void 0,
415
+ onAnimationEnd: N,
416
+ open: !0,
417
+ children: [
418
+ h && /* @__PURE__ */ i.jsx(
419
+ "button",
420
+ {
421
+ type: "button",
422
+ className: b["close-button"],
423
+ onClick: () => v(!1),
424
+ "aria-label": "닫기",
425
+ children: /* @__PURE__ */ i.jsx(
426
+ "svg",
427
+ {
428
+ width: "12",
429
+ height: "12",
430
+ viewBox: "0 0 12 12",
431
+ fill: "none",
432
+ xmlns: "http://www.w3.org/2000/svg",
433
+ children: /* @__PURE__ */ i.jsx(
434
+ "path",
435
+ {
436
+ d: "M9 3L3 9M3 3L9 9",
437
+ stroke: "currentColor",
438
+ strokeWidth: "1.5",
439
+ strokeLinecap: "round",
440
+ strokeLinejoin: "round"
441
+ }
442
+ )
443
+ }
444
+ )
445
+ }
446
+ ),
447
+ /* @__PURE__ */ i.jsxs("div", { className: b.content, children: [
448
+ t && /* @__PURE__ */ i.jsx("div", { id: "okcancel-title", className: b.title, children: t }),
449
+ o && /* @__PURE__ */ i.jsx("div", { id: "okcancel-description", className: b.description, children: o })
450
+ ] }),
451
+ /* @__PURE__ */ i.jsxs("div", { className: b["btn-box"], children: [
452
+ n === "confirm" && /* @__PURE__ */ i.jsx(
453
+ "button",
454
+ {
455
+ type: "button",
456
+ className: b["button-secondary"],
457
+ onClick: () => v(!1),
458
+ children: c
459
+ }
460
+ ),
461
+ /* @__PURE__ */ i.jsx(
462
+ "button",
463
+ {
464
+ type: "button",
465
+ className: b["button-primary"],
466
+ onClick: () => v(!0),
467
+ autoFocus: !0,
468
+ children: a
469
+ }
470
+ )
471
+ ] })
472
+ ]
473
+ }
474
+ ) }) });
475
+ }
476
+ const Se = () => {
477
+ const n = Q(), t = F(
478
+ (a) => n.push(B, {
479
+ type: "confirm",
480
+ ...a,
481
+ resolve: () => {
482
+ }
483
+ }),
484
+ [n]
485
+ ), o = F(
486
+ (a) => n.push(B, {
487
+ type: "alert",
488
+ ...a,
489
+ resolve: () => {
490
+ }
491
+ }),
492
+ [n]
493
+ );
494
+ return { confirm: t, alert: o };
495
+ };
496
+ export {
497
+ Oe as OkCancelProvider,
498
+ Se as useOkCancel
499
+ };
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ (function(){"use strict";try{if(typeof document<"u"){var t=document.createElement("style");t.appendChild(document.createTextNode("._overlay_ht92t_1{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;z-index:var(--okcancel-z-index);background-color:var(--okcancel-overlay-bg)}._overlay_ht92t_1[data-phase=enter]{animation:_okcancel-fade-in_ht92t_1 .4s cubic-bezier(.175,.885,.32,1.275) forwards}._overlay_ht92t_1[data-phase=exit]{animation:_okcancel-fade-out_ht92t_1 .4s cubic-bezier(.68,-.55,.265,1.55) forwards}._dialog_ht92t_17{background:var(--okcancel-dialog-bg);border-radius:var(--okcancel-dialog-border-radius);box-shadow:var(--okcancel-dialog-shadow);max-width:var(--okcancel-dialog-max-width);min-width:var(--okcancel-dialog-min-width);max-height:var(--okcancel-dialog-max-height);min-height:var(--okcancel-dialog-min-height);width:var(--okcancel-dialog-width);height:var(--okcancel-dialog-height);border:var(--okcancel-dialog-border);padding:var(--okcancel-dialog-padding)}._dialog_ht92t_17[data-animation=true][data-phase=enter]{animation:_okcancel-slide-in_ht92t_1 .4s cubic-bezier(.175,.885,.32,1.275) forwards}._dialog_ht92t_17[data-animation=true][data-phase=exit]{animation:_okcancel-slide-out_ht92t_1 .4s cubic-bezier(.68,-.55,.265,1.55) forwards}._dialog_ht92t_17[data-animation=false]{transform:translateY(0)}._dialog_ht92t_17 ._content_ht92t_39{display:flex;flex-direction:var(--okcancel-content-flex-direction);justify-content:var(--okcancel-content-justify-content);align-items:var(--okcancel-content-align-items);gap:var(--okcancel-content-gap);padding:var(--okcancel-content-padding);height:var(--okcancel-content-height);max-height:var(--okcancel-content-max-height);min-height:var(--okcancel-content-min-height)}._dialog_ht92t_17 ._content_ht92t_39 ._title_ht92t_50{font-size:var(--okcancel-title-font-size);font-weight:var(--okcancel-title-font-weight);color:var(--okcancel-title-color)}._dialog_ht92t_17 ._content_ht92t_39 ._description_ht92t_55{font-size:var(--okcancel-description-font-size);font-weight:var(--okcancel-description-font-weight);color:var(--okcancel-description-color)}._dialog_ht92t_17 ._close-button_ht92t_60{position:absolute;top:8px;right:8px;background:transparent;border:none;font-size:20px;cursor:pointer;width:32px;height:32px;display:flex;align-items:center;justify-content:center;color:#000}._dialog_ht92t_17 ._btn-box_ht92t_75{display:flex;flex-direction:var(--okcancel-btn-box-flex-direction);justify-content:var(--okcancel-btn-box-justify-content);align-items:var(--okcancel-btn-box-align-items);gap:var(--okcancel-btn-box-gap);padding:var(--okcancel-btn-box-padding);height:var(--okcancel-btn-box-height)}._dialog_ht92t_17 ._btn-box_ht92t_75 button{border:var(--okcancel-button-border);border-radius:var(--okcancel-button-border-radius);box-shadow:var(--okcancel-button-box-shadow);padding:var(--okcancel-button-padding);font-size:var(--okcancel-button-font-size);font-weight:var(--okcancel-button-font-weight);text-align:var(--okcancel-button-text-align);cursor:pointer;min-width:var(--okcancel-button-min-width);max-width:var(--okcancel-button-max-width);width:var(--okcancel-button-width);height:var(--okcancel-button-height);flex:var(--okcancel-button-flex)}._dialog_ht92t_17 ._btn-box_ht92t_75 button._button-primary_ht92t_99{background-color:var(--okcancel-button-primary-bg);color:var(--okcancel-button-primary-color);border:var(--okcancel-button-primary-border)}._dialog_ht92t_17 ._btn-box_ht92t_75 button._button-secondary_ht92t_104{background-color:var(--okcancel-button-secondary-bg);color:var(--okcancel-button-secondary-color);border:var(--okcancel-button-secondary-border)}@keyframes _okcancel-fade-in_ht92t_1{0%{opacity:0}to{opacity:1}}@keyframes _okcancel-fade-out_ht92t_1{0%{opacity:1}to{opacity:0}}@keyframes _okcancel-slide-in_ht92t_1{0%{transform:translateY(50vh)}to{transform:translateY(0)}}@keyframes _okcancel-slide-out_ht92t_1{0%{transform:translateY(0)}to{transform:translateY(50vh)}}:root{--okcancel-dialog-bg: #fff;--okcancel-dialog-border-radius: 8px;--okcancel-dialog-shadow: 0 20px 25px -5px rgb(0 0 0 / 10%), 0 8px 10px -6px rgb(0 0 0 / 10%);--okcancel-dialog-max-width: none;--okcancel-dialog-min-width: auto;--okcancel-dialog-max-height: none;--okcancel-dialog-min-height: auto;--okcancel-dialog-width: 500px;--okcancel-dialog-height: max-content;--okcancel-dialog-padding: 16px 24px;--okcancel-dialog-border: none;--okcancel-content-padding: 0;--okcancel-content-gap: 8px;--okcancel-content-flex-direction: column;--okcancel-content-justify-content: center;--okcancel-content-align-items: normal;--okcancel-content-height: auto;--okcancel-content-max-height: none;--okcancel-content-min-height: 100px;--okcancel-title-font-size: 20px;--okcancel-title-color: #222;--okcancel-title-font-weight: 600;--okcancel-description-font-size: 16px;--okcancel-description-color: #8e8e8e;--okcancel-description-font-weight: 400;--okcancel-btn-box-padding: 10px 0;--okcancel-btn-box-gap: 12px;--okcancel-btn-box-flex-direction: row;--okcancel-btn-box-justify-content: flex-end;--okcancel-btn-box-align-items: center;--okcancel-btn-box-height: auto;--okcancel-button-padding: 8px 16px;--okcancel-button-border-radius: 6px;--okcancel-button-width: 100px;--okcancel-button-height: 40px;--okcancel-button-max-width: none;--okcancel-button-min-width: auto;--okcancel-button-border: none;--okcancel-button-box-shadow: none;--okcancel-button-font-size: 16px;--okcancel-button-font-weight: 500;--okcancel-button-text-align: center;--okcancel-button-flex: 0 1 auto;--okcancel-button-primary-bg: #2c89e5;--okcancel-button-primary-color: #fff;--okcancel-button-primary-border: none;--okcancel-button-secondary-bg: transparent;--okcancel-button-secondary-color: #222;--okcancel-button-secondary-border: 1px solid #d4d4d4;--okcancel-overlay-bg: rgb(0 0 0 / 50%);--okcancel-z-index: 1000}@media (width < 640px){:root{--okcancel-dialog-width: 85%;--okcancel-btn-box-gap: 20px;--okcancel-button-flex: 1}}#okcancel-portal-root{position:relative;z-index:var(--okcancel-z-index)}")),document.head.appendChild(t)}}catch(o){console.error("vite-plugin-css-injected-by-js",o)}})();
2
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const _=require("react"),ae=require("react-dom");var A={exports:{}},j={};/**
3
+ * @license React
4
+ * react-jsx-runtime.production.js
5
+ *
6
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
7
+ *
8
+ * This source code is licensed under the MIT license found in the
9
+ * LICENSE file in the root directory of this source tree.
10
+ */var z;function ce(){if(z)return j;z=1;var n=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function o(a,c,d){var m=null;if(d!==void 0&&(m=""+d),c.key!==void 0&&(m=""+c.key),"key"in c){d={};for(var b in c)b!=="key"&&(d[b]=c[b])}else d=c;return c=d.ref,{$$typeof:n,type:a,key:m,ref:c!==void 0?c:null,props:d}}return j.Fragment=t,j.jsx=o,j.jsxs=o,j}var y={};/**
11
+ * @license React
12
+ * react-jsx-runtime.development.js
13
+ *
14
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
15
+ *
16
+ * This source code is licensed under the MIT license found in the
17
+ * LICENSE file in the root directory of this source tree.
18
+ */var V;function ie(){return V||(V=1,process.env.NODE_ENV!=="production"&&(function(){function n(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===ne?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case P:return"Fragment";case f:return"Profiler";case C:return"StrictMode";case K:return"Suspense";case ee:return"SuspenseList";case re:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case E:return"Portal";case Z:return(e.displayName||"Context")+".Provider";case O:return(e._context.displayName||"Context")+".Consumer";case Q:var r=e.render;return e=e.displayName,e||(e=r.displayName||r.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case te:return r=e.displayName||null,r!==null?r:n(e.type)||"Memo";case M:r=e._payload,e=e._init;try{return n(e(r))}catch{}}return null}function t(e){return""+e}function o(e){try{t(e);var r=!1}catch{r=!0}if(r){r=console;var s=r.error,l=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return s.call(r,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",l),t(e)}}function a(e){if(e===P)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===M)return"<...>";try{var r=n(e);return r?"<"+r+">":"<...>"}catch{return"<...>"}}function c(){var e=N.A;return e===null?null:e.getOwner()}function d(){return Error("react-stack-top-frame")}function m(e){if(F.call(e,"key")){var r=Object.getOwnPropertyDescriptor(e,"key").get;if(r&&r.isReactWarning)return!1}return e.key!==void 0}function b(e,r){function s(){$||($=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",r))}s.isReactWarning=!0,Object.defineProperty(e,"key",{get:s,configurable:!0})}function p(){var e=n(this.type);return W[e]||(W[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function T(e,r,s,l,R,h,Y,D){return s=h.ref,e={$$typeof:S,type:e,key:r,props:h,_owner:R},(s!==void 0?s:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:p}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:Y}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:D}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function g(e,r,s,l,R,h,Y,D){var u=r.children;if(u!==void 0)if(l)if(oe(u)){for(l=0;l<u.length;l++)w(u[l]);Object.freeze&&Object.freeze(u)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else w(u);if(F.call(r,"key")){u=n(e);var x=Object.keys(r).filter(function(se){return se!=="key"});l=0<x.length?"{key: someKey, "+x.join(": ..., ")+": ...}":"{key: someKey}",J[u+l]||(x=0<x.length?"{"+x.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
19
+ let props = %s;
20
+ <%s {...props} />
21
+ React keys must be passed directly to JSX without using spread:
22
+ let props = %s;
23
+ <%s key={someKey} {...props} />`,l,u,x,u),J[u+l]=!0)}if(u=null,s!==void 0&&(o(s),u=""+s),m(r)&&(o(r.key),u=""+r.key),"key"in r){s={};for(var L in r)L!=="key"&&(s[L]=r[L])}else s=r;return u&&b(s,typeof e=="function"?e.displayName||e.name||"Unknown":e),T(e,u,h,R,c(),s,Y,D)}function w(e){typeof e=="object"&&e!==null&&e.$$typeof===S&&e._store&&(e._store.validated=1)}var k=_,S=Symbol.for("react.transitional.element"),E=Symbol.for("react.portal"),P=Symbol.for("react.fragment"),C=Symbol.for("react.strict_mode"),f=Symbol.for("react.profiler"),O=Symbol.for("react.consumer"),Z=Symbol.for("react.context"),Q=Symbol.for("react.forward_ref"),K=Symbol.for("react.suspense"),ee=Symbol.for("react.suspense_list"),te=Symbol.for("react.memo"),M=Symbol.for("react.lazy"),re=Symbol.for("react.activity"),ne=Symbol.for("react.client.reference"),N=k.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,F=Object.prototype.hasOwnProperty,oe=Array.isArray,I=console.createTask?console.createTask:function(){return null};k={react_stack_bottom_frame:function(e){return e()}};var $,W={},q=k.react_stack_bottom_frame.bind(k,d)(),U=I(a(d)),J={};y.Fragment=P,y.jsx=function(e,r,s,l,R){var h=1e4>N.recentlyCreatedOwnerStacks++;return g(e,r,s,!1,l,R,h?Error("react-stack-top-frame"):q,h?I(a(e)):U)},y.jsxs=function(e,r,s,l,R){var h=1e4>N.recentlyCreatedOwnerStacks++;return g(e,r,s,!0,l,R,h?Error("react-stack-top-frame"):q,h?I(a(e)):U)}})()),y}var G;function le(){return G||(G=1,process.env.NODE_ENV==="production"?A.exports=ce():A.exports=ie()),A.exports}var i=le();const B=_.createContext(null),ue=()=>Math.random().toString(36).substring(2,9);class de{_instances=[];_listeners=new Set;push(t,o,a){return new Promise((c,d)=>{const m=a?.id||ue(),b={id:m,component:t,props:o||{},resolve:p=>{this.removeInstance(m),c(p)},reject:p=>{this.removeInstance(m),d(p)},createdAt:Date.now()};this._instances=[...this._instances,b],this.notify()})}pop(t){if(this._instances.length!==0)if(t)this.reject(t,new Error("Popup closed explicitly"));else{const o=this._instances[this._instances.length-1];this.reject(o.id,new Error("Popup closed explicitly"))}}resolve(t,o){const a=this._instances.find(c=>c.id===t);a&&a.resolve(o)}reject(t,o){const a=this._instances.find(c=>c.id===t);a&&a.reject(o)}getInstances(){return this._instances}subscribe(t){return this._listeners.add(t),t(this._instances),()=>{this._listeners.delete(t)}}removeInstance(t){this._instances=this._instances.filter(o=>o.id!==t),this.notify()}notify(){this._listeners.forEach(t=>t(this._instances))}}const fe=()=>new de,H=()=>{const n=_.useContext(B);if(!n)throw new Error("usePop must be used within a PopProvider");return n},me=()=>{const n=H(),[t,o]=_.useState(n.getInstances());return _.useEffect(()=>n.subscribe(o),[n]),t},_e=()=>{const n=me();return n.length===0?null:i.jsx(i.Fragment,{children:n.map(t=>{const o=t.component;return i.jsx(o,{...t.props},t.id)})})},pe=({children:n})=>{const[t]=_.useState(()=>fe());return i.jsxs(B.Provider,{value:t,children:[n,i.jsx(_e,{})]})};function he({children:n}){return i.jsx(pe,{children:n})}function be({children:n}){let t=document.getElementById("okcancel-portal-root");return t||(t=document.createElement("div"),t.id="okcancel-portal-root",document.body.appendChild(t)),ae.createPortal(n,t)}const ve="_overlay_ht92t_1",Ee="_dialog_ht92t_17",Re="_content_ht92t_39",ke="_title_ht92t_50",ge="_description_ht92t_55",v={overlay:ve,dialog:Ee,content:Re,title:ke,description:ge,"close-button":"_close-button_ht92t_60","btn-box":"_btn-box_ht92t_75","button-primary":"_button-primary_ht92t_99","button-secondary":"_button-secondary_ht92t_104"};function X({type:n,title:t,description:o,confirmText:a,cancelText:c,canCloseOnOverlay:d=!0,canCloseOnEsc:m=!0,showCloseButton:b=!1,enableAnimation:p=!0,resolve:T}){const[g,w]=_.useState("enter"),[k,S]=_.useState(null),E=_.useCallback(f=>{if(!p){T(n==="confirm"?f:void 0);return}S(f),w("exit")},[p,T,n]),P=f=>{f.target===f.currentTarget&&d&&E(!1)},C=f=>{p&&f.target===f.currentTarget&&g==="exit"&&k!==null&&T(n==="confirm"?k:void 0)};return _.useEffect(()=>{const f=O=>{O.key==="Escape"&&m&&(O.preventDefault(),E(!1))};return document.addEventListener("keydown",f),()=>document.removeEventListener("keydown",f)},[m,E]),i.jsx(be,{children:i.jsx("div",{className:v.overlay,"data-phase":g,onClick:P,children:i.jsxs("dialog",{className:v.dialog,"data-phase":g,"data-animation":p??!0,"aria-labelledby":t?"okcancel-title":void 0,"aria-describedby":o?"okcancel-description":void 0,onAnimationEnd:C,open:!0,children:[b&&i.jsx("button",{type:"button",className:v["close-button"],onClick:()=>E(!1),"aria-label":"닫기",children:i.jsx("svg",{width:"12",height:"12",viewBox:"0 0 12 12",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:i.jsx("path",{d:"M9 3L3 9M3 3L9 9",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}),i.jsxs("div",{className:v.content,children:[t&&i.jsx("div",{id:"okcancel-title",className:v.title,children:t}),o&&i.jsx("div",{id:"okcancel-description",className:v.description,children:o})]}),i.jsxs("div",{className:v["btn-box"],children:[n==="confirm"&&i.jsx("button",{type:"button",className:v["button-secondary"],onClick:()=>E(!1),children:c}),i.jsx("button",{type:"button",className:v["button-primary"],onClick:()=>E(!0),autoFocus:!0,children:a})]})]})})})}const xe=()=>{const n=H(),t=_.useCallback(a=>n.push(X,{type:"confirm",...a,resolve:()=>{}}),[n]),o=_.useCallback(a=>n.push(X,{type:"alert",...a,resolve:()=>{}}),[n]);return{confirm:t,alert:o}};exports.OkCancelProvider=he;exports.useOkCancel=xe;
@@ -0,0 +1 @@
1
+ export default function App(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { PopManager } from './types';
2
+ export declare const PopContext: import('react').Context<PopManager | null>;
@@ -0,0 +1,3 @@
1
+ import { PopInstance } from './types';
2
+ export declare const usePop: () => import('./types').PopManager;
3
+ export declare const usePopState: () => PopInstance<unknown, unknown>[];
@@ -0,0 +1,6 @@
1
+ import { ReactNode } from 'react';
2
+ interface PopProviderProps {
3
+ children: ReactNode;
4
+ }
5
+ export declare const PopProvider: ({ children }: PopProviderProps) => import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,16 @@
1
+ import { PopManager, PopInstance, PopId } from './types';
2
+ export declare class DefaultPopManager implements PopManager {
3
+ private _instances;
4
+ private _listeners;
5
+ push<TProps, TResult>(component: React.ComponentType<TProps>, props?: TProps, options?: {
6
+ id?: string;
7
+ }): Promise<TResult>;
8
+ pop(id?: PopId): void;
9
+ resolve<TResult>(id: PopId, result: TResult): void;
10
+ reject(id: PopId, reason?: unknown): void;
11
+ getInstances(): PopInstance[];
12
+ subscribe(callback: (instances: PopInstance[]) => void): () => void;
13
+ private removeInstance;
14
+ private notify;
15
+ }
16
+ export declare const createPopManager: () => PopManager;
@@ -0,0 +1,59 @@
1
+ import { ComponentType } from 'react';
2
+ /**
3
+ * Unique identifier for each popup instance
4
+ */
5
+ export type PopId = string;
6
+ /**
7
+ * Base options for creating a popup
8
+ */
9
+ export interface PopOptions<TProps = Record<string, unknown>> {
10
+ /** Unique ID (auto-generated if not provided) */
11
+ id?: PopId;
12
+ /** Component to render */
13
+ component: ComponentType<TProps>;
14
+ /** Props to pass to the component */
15
+ props?: TProps;
16
+ }
17
+ /**
18
+ * Internal instance structure stored in the manager
19
+ */
20
+ export interface PopInstance<TProps = unknown, TResult = unknown> {
21
+ id: PopId;
22
+ component: ComponentType<TProps>;
23
+ props: TProps;
24
+ resolve: (result: TResult) => void;
25
+ reject: (reason?: unknown) => void;
26
+ createdAt: number;
27
+ }
28
+ /**
29
+ * Core Logic Manager Interface
30
+ */
31
+ export interface PopManager {
32
+ /**
33
+ * Push a new popup to the stack
34
+ * @returns A promise that resolves when the popup is closed/resolved
35
+ */
36
+ push<TProps, TResult>(component: ComponentType<TProps>, props?: TProps, options?: {
37
+ id?: string;
38
+ }): Promise<TResult>;
39
+ /**
40
+ * Remove a specific popup or the top one
41
+ */
42
+ pop(id?: PopId): void;
43
+ /**
44
+ * Resolve a popup with a value (success)
45
+ */
46
+ resolve<TResult>(id: PopId, result: TResult): void;
47
+ /**
48
+ * Reject a popup with a reason (failure/cancel)
49
+ */
50
+ reject(id: PopId, reason?: unknown): void;
51
+ /**
52
+ * Current stack of instances
53
+ */
54
+ getInstances(): PopInstance[];
55
+ /**
56
+ * Subscribe to changes (Observer pattern)
57
+ */
58
+ subscribe(callback: (instances: PopInstance[]) => void): () => void;
59
+ }
@@ -0,0 +1,3 @@
1
+ export { default as OkCancelProvider } from './presets/default/provider';
2
+ export { useOkCancel } from './presets/default/hooks';
3
+ export * from './presets/default/types';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import { ConfirmOptions } from '../types';
2
+ export interface DialogProps extends ConfirmOptions {
3
+ type: 'confirm' | 'alert';
4
+ resolve: (value: unknown) => void;
5
+ }
6
+ export default function Dialog({ type, title, description, confirmText, cancelText, canCloseOnOverlay, canCloseOnEsc, showCloseButton, enableAnimation, resolve, }: DialogProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { ReactNode } from 'react';
2
+ interface PortalProps {
3
+ children: ReactNode;
4
+ }
5
+ export default function Portal({ children }: PortalProps): import('react').ReactPortal;
6
+ export {};
@@ -0,0 +1,2 @@
1
+ import { OkCancelContextValue } from './types';
2
+ export declare const useOkCancel: () => OkCancelContextValue;
@@ -0,0 +1,6 @@
1
+ import { ReactNode } from 'react';
2
+ interface OkCancelProviderProps {
3
+ children: ReactNode;
4
+ }
5
+ export default function OkCancelProvider({ children }: OkCancelProviderProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,37 @@
1
+ import { ReactNode } from 'react';
2
+ export interface BaseDialogOptions {
3
+ title?: ReactNode;
4
+ description?: ReactNode;
5
+ showCloseButton?: boolean;
6
+ enableAnimation?: boolean;
7
+ }
8
+ export interface ConfirmOptions extends BaseDialogOptions {
9
+ confirmText?: ReactNode;
10
+ cancelText?: ReactNode;
11
+ canCloseOnOverlay?: boolean;
12
+ canCloseOnEsc?: boolean;
13
+ }
14
+ export interface AlertOptions extends BaseDialogOptions {
15
+ confirmText?: ReactNode;
16
+ canCloseOnOverlay?: boolean;
17
+ canCloseOnEsc?: boolean;
18
+ }
19
+ export interface DialogState {
20
+ type: 'confirm' | 'alert' | null;
21
+ title?: ReactNode;
22
+ description?: ReactNode;
23
+ confirmText?: ReactNode;
24
+ cancelText?: ReactNode;
25
+ canCloseOnOverlay?: boolean;
26
+ canCloseOnEsc?: boolean;
27
+ showCloseButton?: boolean;
28
+ enableAnimation?: boolean;
29
+ resolve?: (value: boolean | void) => void;
30
+ }
31
+ export interface OkCancelContextValue {
32
+ confirm: (options: ConfirmOptions) => Promise<boolean>;
33
+ alert: (options: AlertOptions) => Promise<void>;
34
+ }
35
+ export interface OkCancelProviderProps {
36
+ children: ReactNode;
37
+ }
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@pop-kit/okcancel",
3
+ "version": "0.0.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "type": "module",
8
+ "main": "dist/index.cjs",
9
+ "module": "dist/index.esm.js",
10
+ "types": "dist/types/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/types/index.d.ts",
14
+ "import": "./dist/index.esm.js",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/masimaru/react-okcancel"
24
+ },
25
+ "license": "MIT",
26
+ "scripts": {
27
+ "dev": "vite",
28
+ "build": "tsc -b && vite build",
29
+ "preview": "vite preview",
30
+ "lint:fix": "eslint src --ext .js,.jsx,.ts,.tsx --fix",
31
+ "lint:css:fix": "stylelint \"src/**/*.{css,scss}\" --fix",
32
+ "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,css,scss,md,json}\""
33
+ },
34
+ "peerDependencies": {
35
+ "react": ">=17",
36
+ "react-dom": ">=17"
37
+ },
38
+ "devDependencies": {
39
+ "@eslint/js": "^9.33.0",
40
+ "@semantic-release/commit-analyzer": "^13.0.1",
41
+ "@semantic-release/github": "^11.0.6",
42
+ "@semantic-release/npm": "^12.0.2",
43
+ "@semantic-release/release-notes-generator": "^14.1.0",
44
+ "@types/react": "^19.1.10",
45
+ "@types/react-dom": "^19.1.7",
46
+ "@vitejs/plugin-react": "^5.0.0",
47
+ "conventional-changelog-conventionalcommits": "^9.1.0",
48
+ "eslint": "^9.33.0",
49
+ "eslint-config-prettier": "^10.1.8",
50
+ "eslint-plugin-prettier": "^5.5.4",
51
+ "eslint-plugin-react-hooks": "^5.2.0",
52
+ "eslint-plugin-react-refresh": "^0.4.20",
53
+ "globals": "^16.3.0",
54
+ "prettier": "^3.6.2",
55
+ "sass": "^1.91.0",
56
+ "semantic-release": "^24.2.8",
57
+ "stylelint": "^16.23.1",
58
+ "stylelint-config-prettier-scss": "^1.0.0",
59
+ "stylelint-config-standard-scss": "^15.0.1",
60
+ "typescript": "~5.8.3",
61
+ "typescript-eslint": "^8.39.1",
62
+ "vite": "^7.1.2",
63
+ "vite-plugin-css-injected-by-js": "^3.5.2",
64
+ "vite-plugin-dts": "^4.5.4",
65
+ "vite-tsconfig-paths": "^5.1.4"
66
+ }
67
+ }