@djangocfg/ui-core 2.1.242 → 2.1.246
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 +15 -7
- package/package.json +4 -4
- package/src/lib/dialog-service/DialogProvider.tsx +8 -47
- package/src/lib/dialog-service/hooks/use-dialog.ts +26 -14
- package/src/lib/dialog-service/index.ts +4 -3
- package/src/lib/dialog-service/store.ts +133 -0
- package/src/lib/dialog-service/types.ts +9 -12
- package/src/lib/dialog-service/events.ts +0 -73
package/README.md
CHANGED
|
@@ -149,12 +149,12 @@ hslToRgba('217 91% 60%', 0.5); // 'rgba(59, 130, 246, 0.5)'
|
|
|
149
149
|
|
|
150
150
|
## Dialog Service
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
Zustand-powered dialog service replacing native `window.alert`, `window.confirm`, `window.prompt` with shadcn dialogs. Also provides `window.dialog.auth()` for triggering authentication dialogs.
|
|
153
153
|
|
|
154
154
|
```tsx
|
|
155
|
-
import { DialogProvider, useDialog
|
|
155
|
+
import { DialogProvider, useDialog } from '@djangocfg/ui-core/lib/dialog-service';
|
|
156
156
|
|
|
157
|
-
// Wrap your app with DialogProvider
|
|
157
|
+
// Wrap your app with DialogProvider (already included in BaseApp)
|
|
158
158
|
function App() {
|
|
159
159
|
return (
|
|
160
160
|
<DialogProvider>
|
|
@@ -165,7 +165,7 @@ function App() {
|
|
|
165
165
|
|
|
166
166
|
// Use via React hook
|
|
167
167
|
function Component() {
|
|
168
|
-
const { alert, confirm, prompt } = useDialog();
|
|
168
|
+
const { alert, confirm, prompt, auth } = useDialog();
|
|
169
169
|
|
|
170
170
|
const handleDelete = async () => {
|
|
171
171
|
const confirmed = await confirm({
|
|
@@ -177,12 +177,20 @@ function Component() {
|
|
|
177
177
|
// Delete...
|
|
178
178
|
}
|
|
179
179
|
};
|
|
180
|
+
|
|
181
|
+
const handleProtected = async () => {
|
|
182
|
+
const didAuth = await auth({ message: 'Please sign in to continue' });
|
|
183
|
+
if (didAuth) {
|
|
184
|
+
// User navigated to auth
|
|
185
|
+
}
|
|
186
|
+
};
|
|
180
187
|
}
|
|
181
188
|
|
|
182
189
|
// Or use globally from anywhere (vanilla JS, libraries, etc.)
|
|
183
|
-
dialog.alert({ message: 'Hello!' });
|
|
184
|
-
const ok = await dialog.confirm({ message: 'Are you sure?' });
|
|
185
|
-
const name = await dialog.prompt({ message: 'Enter your name:' });
|
|
190
|
+
window.dialog.alert({ message: 'Hello!' });
|
|
191
|
+
const ok = await window.dialog.confirm({ message: 'Are you sure?' });
|
|
192
|
+
const name = await window.dialog.prompt({ message: 'Enter your name:' });
|
|
193
|
+
const didAuth = await window.dialog.auth({ message: 'Session expired' });
|
|
186
194
|
```
|
|
187
195
|
|
|
188
196
|
## Usage
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.246",
|
|
4
4
|
"description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-components",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"playground": "playground dev"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
|
-
"@djangocfg/i18n": "^2.1.
|
|
84
|
+
"@djangocfg/i18n": "^2.1.246",
|
|
85
85
|
"consola": "^3.4.2",
|
|
86
86
|
"lucide-react": "^0.545.0",
|
|
87
87
|
"moment": "^2.30.1",
|
|
@@ -143,9 +143,9 @@
|
|
|
143
143
|
"vaul": "1.1.2"
|
|
144
144
|
},
|
|
145
145
|
"devDependencies": {
|
|
146
|
-
"@djangocfg/i18n": "^2.1.
|
|
146
|
+
"@djangocfg/i18n": "^2.1.246",
|
|
147
147
|
"@djangocfg/playground": "workspace:*",
|
|
148
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
148
|
+
"@djangocfg/typescript-config": "^2.1.246",
|
|
149
149
|
"@types/node": "^24.7.2",
|
|
150
150
|
"@types/react": "^19.1.0",
|
|
151
151
|
"@types/react-dom": "^19.1.0",
|
|
@@ -1,73 +1,34 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useEffect,
|
|
3
|
+
import { useEffect, useCallback, type ReactNode } from 'react';
|
|
4
4
|
import { AlertDialogUI, ConfirmDialogUI, PromptDialogUI } from './dialogs';
|
|
5
|
-
import { initDialogAPI,
|
|
6
|
-
import { DIALOG_REQUEST_EVENT } from './constants';
|
|
7
|
-
import type { DialogRequest, DialogRequestPayload } from './types';
|
|
5
|
+
import { initDialogAPI, useDialogStore } from './store';
|
|
8
6
|
|
|
9
7
|
interface DialogProviderProps {
|
|
10
8
|
children: ReactNode;
|
|
11
9
|
}
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
|
-
* DialogProvider -
|
|
12
|
+
* DialogProvider - Reads from zustand dialog store and renders appropriate dialogs
|
|
15
13
|
*
|
|
16
14
|
* Must be mounted once at the app root (e.g., in BaseApp).
|
|
17
15
|
* Handles dialog queue to show one dialog at a time.
|
|
18
16
|
*/
|
|
19
17
|
export function DialogProvider({ children }: DialogProviderProps) {
|
|
20
|
-
const
|
|
21
|
-
const
|
|
18
|
+
const current = useDialogStore((s) => s.current);
|
|
19
|
+
const resolve = useDialogStore((s) => s.resolve);
|
|
22
20
|
|
|
23
|
-
// Initialize global API on mount
|
|
21
|
+
// Initialize global window.dialog API on mount
|
|
24
22
|
useEffect(() => {
|
|
25
23
|
initDialogAPI();
|
|
26
24
|
}, []);
|
|
27
25
|
|
|
28
|
-
// Listen for dialog requests
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
const handleRequest = (event: Event) => {
|
|
31
|
-
const customEvent = event as CustomEvent<DialogRequestPayload>;
|
|
32
|
-
const { id, type, options } = customEvent.detail;
|
|
33
|
-
|
|
34
|
-
const request: DialogRequest = {
|
|
35
|
-
id,
|
|
36
|
-
type,
|
|
37
|
-
options,
|
|
38
|
-
resolve: (result) => {
|
|
39
|
-
dispatchDialogResponse(id, result);
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
setQueue((prev) => [...prev, request]);
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
window.addEventListener(DIALOG_REQUEST_EVENT, handleRequest);
|
|
47
|
-
|
|
48
|
-
return () => {
|
|
49
|
-
window.removeEventListener(DIALOG_REQUEST_EVENT, handleRequest);
|
|
50
|
-
};
|
|
51
|
-
}, []);
|
|
52
|
-
|
|
53
|
-
// Process queue - show one dialog at a time
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
if (!current && queue.length > 0) {
|
|
56
|
-
const [next, ...rest] = queue;
|
|
57
|
-
setCurrent(next ?? null);
|
|
58
|
-
setQueue(rest);
|
|
59
|
-
}
|
|
60
|
-
}, [current, queue]);
|
|
61
|
-
|
|
62
26
|
// Handle dialog close
|
|
63
27
|
const handleClose = useCallback(
|
|
64
28
|
(result: boolean | string | null) => {
|
|
65
|
-
|
|
66
|
-
current.resolve(result);
|
|
67
|
-
setCurrent(null);
|
|
68
|
-
}
|
|
29
|
+
resolve(result);
|
|
69
30
|
},
|
|
70
|
-
[
|
|
31
|
+
[resolve],
|
|
71
32
|
);
|
|
72
33
|
|
|
73
34
|
// Render current dialog
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useCallback } from 'react';
|
|
4
|
-
import
|
|
4
|
+
import { showDialog, dialogStore } from '../store';
|
|
5
|
+
import type { DialogOptions, AuthDialogOptions } from '../types';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* React hook for using dialog service
|
|
8
9
|
*
|
|
9
|
-
* Provides type-safe access to
|
|
10
|
+
* Provides type-safe access to dialogs via zustand store with fallback to native dialogs.
|
|
10
11
|
*
|
|
11
12
|
* @example
|
|
12
13
|
* ```tsx
|
|
13
14
|
* function MyComponent() {
|
|
14
|
-
* const { confirm, alert, prompt } = useDialog();
|
|
15
|
+
* const { confirm, alert, prompt, auth } = useDialog();
|
|
15
16
|
*
|
|
16
17
|
* const handleDelete = async () => {
|
|
17
18
|
* const confirmed = await confirm({
|
|
@@ -24,6 +25,13 @@ import type { DialogOptions } from '../types';
|
|
|
24
25
|
* }
|
|
25
26
|
* };
|
|
26
27
|
*
|
|
28
|
+
* const handleProtected = async () => {
|
|
29
|
+
* const authed = await auth({ message: 'Please sign in to continue' });
|
|
30
|
+
* if (authed) {
|
|
31
|
+
* // proceed
|
|
32
|
+
* }
|
|
33
|
+
* };
|
|
34
|
+
*
|
|
27
35
|
* return <button onClick={handleDelete}>Delete</button>;
|
|
28
36
|
* }
|
|
29
37
|
* ```
|
|
@@ -35,13 +43,12 @@ export function useDialog() {
|
|
|
35
43
|
return Promise.resolve();
|
|
36
44
|
}
|
|
37
45
|
if (!window.dialog) {
|
|
38
|
-
// Fallback to native
|
|
39
46
|
window.alert(typeof message === 'string' ? message : message.message);
|
|
40
47
|
return Promise.resolve();
|
|
41
48
|
}
|
|
42
|
-
return
|
|
49
|
+
return showDialog('alert', message).then(() => undefined);
|
|
43
50
|
},
|
|
44
|
-
[]
|
|
51
|
+
[],
|
|
45
52
|
);
|
|
46
53
|
|
|
47
54
|
const confirm = useCallback(
|
|
@@ -50,14 +57,13 @@ export function useDialog() {
|
|
|
50
57
|
return Promise.resolve(false);
|
|
51
58
|
}
|
|
52
59
|
if (!window.dialog) {
|
|
53
|
-
// Fallback to native
|
|
54
60
|
return Promise.resolve(
|
|
55
|
-
window.confirm(typeof message === 'string' ? message : message.message)
|
|
61
|
+
window.confirm(typeof message === 'string' ? message : message.message),
|
|
56
62
|
);
|
|
57
63
|
}
|
|
58
|
-
return
|
|
64
|
+
return showDialog('confirm', message) as Promise<boolean>;
|
|
59
65
|
},
|
|
60
|
-
[]
|
|
66
|
+
[],
|
|
61
67
|
);
|
|
62
68
|
|
|
63
69
|
const prompt = useCallback(
|
|
@@ -66,14 +72,20 @@ export function useDialog() {
|
|
|
66
72
|
return Promise.resolve(null);
|
|
67
73
|
}
|
|
68
74
|
if (!window.dialog) {
|
|
69
|
-
// Fallback to native
|
|
70
75
|
const opts = typeof message === 'string' ? { message } : message;
|
|
71
76
|
return Promise.resolve(window.prompt(opts.message, opts.defaultValue));
|
|
72
77
|
}
|
|
73
|
-
return
|
|
78
|
+
return showDialog('prompt', message) as Promise<string | null>;
|
|
79
|
+
},
|
|
80
|
+
[],
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const auth = useCallback(
|
|
84
|
+
(options?: AuthDialogOptions): Promise<boolean> => {
|
|
85
|
+
return dialogStore.getState().openAuth(options);
|
|
74
86
|
},
|
|
75
|
-
[]
|
|
87
|
+
[],
|
|
76
88
|
);
|
|
77
89
|
|
|
78
|
-
return { alert, confirm, prompt };
|
|
90
|
+
return { alert, confirm, prompt, auth };
|
|
79
91
|
}
|
|
@@ -3,14 +3,15 @@ export type {
|
|
|
3
3
|
DialogType,
|
|
4
4
|
DialogVariant,
|
|
5
5
|
DialogOptions,
|
|
6
|
+
AuthDialogOptions,
|
|
6
7
|
DialogAPI,
|
|
7
8
|
} from './types';
|
|
8
9
|
|
|
10
|
+
// Store
|
|
11
|
+
export { dialogStore, useDialogStore, initDialogAPI, showDialog } from './store';
|
|
12
|
+
|
|
9
13
|
// Provider
|
|
10
14
|
export { DialogProvider } from './DialogProvider';
|
|
11
15
|
|
|
12
16
|
// Hooks
|
|
13
17
|
export { useDialog } from './hooks';
|
|
14
|
-
|
|
15
|
-
// Events (for advanced usage)
|
|
16
|
-
export { initDialogAPI } from './events';
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createStore, useStore } from 'zustand';
|
|
4
|
+
import type { DialogRequest, DialogType, DialogOptions, AuthDialogOptions } from './types';
|
|
5
|
+
|
|
6
|
+
// ---------- store shape ----------
|
|
7
|
+
|
|
8
|
+
interface DialogState {
|
|
9
|
+
/** Queue of pending dialogs */
|
|
10
|
+
queue: DialogRequest[];
|
|
11
|
+
/** Currently displayed dialog */
|
|
12
|
+
current: DialogRequest | null;
|
|
13
|
+
|
|
14
|
+
/** Auth dialog state (separate — rendered by layouts, not dialog-service) */
|
|
15
|
+
authOpen: boolean;
|
|
16
|
+
authOptions: AuthDialogOptions | null;
|
|
17
|
+
authResolve: ((value: boolean) => void) | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface DialogActions {
|
|
21
|
+
/** Push a standard dialog (alert/confirm/prompt) into the queue */
|
|
22
|
+
enqueue: (request: DialogRequest) => void;
|
|
23
|
+
/** Resolve & close the current standard dialog */
|
|
24
|
+
resolve: (result: boolean | string | null) => void;
|
|
25
|
+
|
|
26
|
+
/** Open auth dialog, returns promise resolved on close */
|
|
27
|
+
openAuth: (options?: AuthDialogOptions) => Promise<boolean>;
|
|
28
|
+
/** Resolve auth dialog (called by AuthDialog component) */
|
|
29
|
+
resolveAuth: (result: boolean) => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type DialogStore = DialogState & DialogActions;
|
|
33
|
+
|
|
34
|
+
// ---------- singleton store ----------
|
|
35
|
+
|
|
36
|
+
let idCounter = 0;
|
|
37
|
+
function generateId(): string {
|
|
38
|
+
return `dialog-${Date.now()}-${++idCounter}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const dialogStore = createStore<DialogStore>((set, get) => ({
|
|
42
|
+
// state
|
|
43
|
+
queue: [],
|
|
44
|
+
current: null,
|
|
45
|
+
authOpen: false,
|
|
46
|
+
authOptions: null,
|
|
47
|
+
authResolve: null,
|
|
48
|
+
|
|
49
|
+
// actions
|
|
50
|
+
enqueue: (request) => {
|
|
51
|
+
set((s) => {
|
|
52
|
+
if (!s.current) {
|
|
53
|
+
return { current: request };
|
|
54
|
+
}
|
|
55
|
+
return { queue: [...s.queue, request] };
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
resolve: (result) => {
|
|
60
|
+
const { current, queue } = get();
|
|
61
|
+
if (!current) return;
|
|
62
|
+
current.resolve(result);
|
|
63
|
+
const [next, ...rest] = queue;
|
|
64
|
+
set({ current: next ?? null, queue: rest });
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
openAuth: (options) => {
|
|
68
|
+
return new Promise<boolean>((resolve) => {
|
|
69
|
+
set({ authOpen: true, authOptions: options ?? null, authResolve: resolve });
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
resolveAuth: (result) => {
|
|
74
|
+
const { authResolve } = get();
|
|
75
|
+
authResolve?.(result);
|
|
76
|
+
set({ authOpen: false, authOptions: null, authResolve: null });
|
|
77
|
+
},
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
// ---------- helpers ----------
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Show a standard dialog (alert/confirm/prompt) via the store.
|
|
84
|
+
* Returns a promise that resolves when the user responds.
|
|
85
|
+
*/
|
|
86
|
+
export function showDialog(
|
|
87
|
+
type: DialogType,
|
|
88
|
+
messageOrOptions: string | DialogOptions,
|
|
89
|
+
): Promise<boolean | string | null> {
|
|
90
|
+
if (type === 'auth') {
|
|
91
|
+
return dialogStore.getState().openAuth(
|
|
92
|
+
typeof messageOrOptions === 'string'
|
|
93
|
+
? { message: messageOrOptions }
|
|
94
|
+
: { message: messageOrOptions.message },
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return new Promise((resolve) => {
|
|
99
|
+
const options: DialogOptions =
|
|
100
|
+
typeof messageOrOptions === 'string'
|
|
101
|
+
? { message: messageOrOptions }
|
|
102
|
+
: messageOrOptions;
|
|
103
|
+
|
|
104
|
+
const request: DialogRequest = {
|
|
105
|
+
id: generateId(),
|
|
106
|
+
type,
|
|
107
|
+
options,
|
|
108
|
+
resolve,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
dialogStore.getState().enqueue(request);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ---------- window.dialog init ----------
|
|
116
|
+
|
|
117
|
+
export function initDialogAPI(): void {
|
|
118
|
+
if (typeof window === 'undefined') return;
|
|
119
|
+
if (window.dialog) return;
|
|
120
|
+
|
|
121
|
+
window.dialog = {
|
|
122
|
+
alert: (message) => showDialog('alert', message).then(() => undefined),
|
|
123
|
+
confirm: (message) => showDialog('confirm', message) as Promise<boolean>,
|
|
124
|
+
prompt: (message) => showDialog('prompt', message) as Promise<string | null>,
|
|
125
|
+
auth: (options) => dialogStore.getState().openAuth(options),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---------- react hooks ----------
|
|
130
|
+
|
|
131
|
+
export function useDialogStore<T>(selector: (state: DialogStore) => T): T {
|
|
132
|
+
return useStore(dialogStore, selector);
|
|
133
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
-
export type DialogType = 'alert' | 'confirm' | 'prompt';
|
|
3
|
+
export type DialogType = 'alert' | 'confirm' | 'prompt' | 'auth';
|
|
4
4
|
|
|
5
5
|
export type DialogVariant = 'default' | 'destructive' | 'warning' | 'success';
|
|
6
6
|
|
|
@@ -27,22 +27,18 @@ export interface DialogOptions {
|
|
|
27
27
|
preventClose?: boolean;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export interface
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
export interface AuthDialogOptions {
|
|
31
|
+
/** Custom message to display */
|
|
32
|
+
message?: string;
|
|
33
|
+
/** URL to redirect after auth */
|
|
34
|
+
redirectUrl?: string;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export interface
|
|
37
|
+
export interface DialogRequest {
|
|
38
38
|
id: string;
|
|
39
39
|
type: DialogType;
|
|
40
40
|
options: DialogOptions;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
export interface DialogResponsePayload {
|
|
44
|
-
id: string;
|
|
45
|
-
result: boolean | string | null;
|
|
41
|
+
resolve: (value: boolean | string | null) => void;
|
|
46
42
|
}
|
|
47
43
|
|
|
48
44
|
/** Global window.dialog API */
|
|
@@ -50,6 +46,7 @@ export interface DialogAPI {
|
|
|
50
46
|
alert: (message: string | DialogOptions) => Promise<void>;
|
|
51
47
|
confirm: (message: string | DialogOptions) => Promise<boolean>;
|
|
52
48
|
prompt: (message: string | DialogOptions) => Promise<string | null>;
|
|
49
|
+
auth: (options?: AuthDialogOptions) => Promise<boolean>;
|
|
53
50
|
}
|
|
54
51
|
|
|
55
52
|
declare global {
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { DIALOG_REQUEST_EVENT, DIALOG_RESPONSE_EVENT } from './constants';
|
|
2
|
-
import type {
|
|
3
|
-
DialogType,
|
|
4
|
-
DialogOptions,
|
|
5
|
-
DialogRequestPayload,
|
|
6
|
-
DialogResponsePayload,
|
|
7
|
-
} from './types';
|
|
8
|
-
|
|
9
|
-
let dialogIdCounter = 0;
|
|
10
|
-
|
|
11
|
-
function generateId(): string {
|
|
12
|
-
return `dialog-${Date.now()}-${++dialogIdCounter}`;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Dispatch dialog request and wait for response via CustomEvent
|
|
17
|
-
*/
|
|
18
|
-
function showDialog(
|
|
19
|
-
type: DialogType,
|
|
20
|
-
messageOrOptions: string | DialogOptions
|
|
21
|
-
): Promise<boolean | string | null> {
|
|
22
|
-
return new Promise((resolve) => {
|
|
23
|
-
const id = generateId();
|
|
24
|
-
const options: DialogOptions =
|
|
25
|
-
typeof messageOrOptions === 'string'
|
|
26
|
-
? { message: messageOrOptions }
|
|
27
|
-
: messageOrOptions;
|
|
28
|
-
|
|
29
|
-
// Listen for response
|
|
30
|
-
const handleResponse = (event: Event) => {
|
|
31
|
-
const customEvent = event as CustomEvent<DialogResponsePayload>;
|
|
32
|
-
if (customEvent.detail.id === id) {
|
|
33
|
-
window.removeEventListener(DIALOG_RESPONSE_EVENT, handleResponse);
|
|
34
|
-
resolve(customEvent.detail.result);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
window.addEventListener(DIALOG_RESPONSE_EVENT, handleResponse);
|
|
39
|
-
|
|
40
|
-
// Dispatch request
|
|
41
|
-
window.dispatchEvent(
|
|
42
|
-
new CustomEvent<DialogRequestPayload>(DIALOG_REQUEST_EVENT, {
|
|
43
|
-
detail: { id, type, options },
|
|
44
|
-
})
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Initialize global window.dialog API
|
|
51
|
-
* Uses singleton pattern - only initializes once
|
|
52
|
-
*/
|
|
53
|
-
export function initDialogAPI(): void {
|
|
54
|
-
if (typeof window === 'undefined') return;
|
|
55
|
-
if (window.dialog) return; // Already initialized
|
|
56
|
-
|
|
57
|
-
window.dialog = {
|
|
58
|
-
alert: (message) => showDialog('alert', message).then(() => undefined),
|
|
59
|
-
confirm: (message) => showDialog('confirm', message) as Promise<boolean>,
|
|
60
|
-
prompt: (message) => showDialog('prompt', message) as Promise<string | null>,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Dispatch response event (used by DialogProvider)
|
|
66
|
-
*/
|
|
67
|
-
export function dispatchDialogResponse(id: string, result: boolean | string | null): void {
|
|
68
|
-
window.dispatchEvent(
|
|
69
|
-
new CustomEvent<DialogResponsePayload>(DIALOG_RESPONSE_EVENT, {
|
|
70
|
-
detail: { id, result },
|
|
71
|
-
})
|
|
72
|
-
);
|
|
73
|
-
}
|