@lerx/promise-modal 0.2.8 → 0.3.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/dist/bootstrap/BootstrapProvider/BootstrapProvider.d.ts +115 -0
- package/dist/bootstrap/BootstrapProvider/useBootstrap.d.ts +182 -0
- package/dist/core/handle/alert.d.ts +144 -3
- package/dist/core/handle/confirm.d.ts +150 -3
- package/dist/core/handle/prompt.d.ts +219 -7
- package/dist/hooks/useActiveModalCount.d.ts +163 -0
- package/dist/hooks/useDestroyAfter.d.ts +127 -0
- package/dist/hooks/useModalAnimation.d.ts +212 -0
- package/dist/hooks/useSubscribeModal.d.ts +114 -0
- package/dist/index.cjs +12 -12
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +13 -13
- package/dist/providers/ConfigurationContext/useConfigurationContext.d.ts +273 -0
- package/package.json +9 -10
|
@@ -1,4 +1,119 @@
|
|
|
1
1
|
import type { BootstrapProviderHandle } from './type';
|
|
2
|
+
/**
|
|
3
|
+
* Provider component that bootstraps the promise-modal system.
|
|
4
|
+
*
|
|
5
|
+
* Sets up the modal rendering infrastructure by creating a portal anchor point
|
|
6
|
+
* and managing the lifecycle of modal components. Can be initialized automatically
|
|
7
|
+
* on mount or manually via ref handle.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* Basic usage with automatic initialization:
|
|
11
|
+
* ```tsx
|
|
12
|
+
* import { ModalProvider, alert, confirm } from '@lerx/promise-modal';
|
|
13
|
+
*
|
|
14
|
+
* function App() {
|
|
15
|
+
* return (
|
|
16
|
+
* <ModalProvider>
|
|
17
|
+
* <button onClick={() => alert({ title: 'Hello!' })}>Alert</button>
|
|
18
|
+
* <button onClick={() => confirm({ title: 'Confirm?' })}>Confirm</button>
|
|
19
|
+
* </ModalProvider>
|
|
20
|
+
* );
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* Custom modal components:
|
|
26
|
+
* ```tsx
|
|
27
|
+
* const CustomForeground = ({ children, visible }) => (
|
|
28
|
+
* <div className={`modal ${visible ? 'show' : 'hide'}`}>
|
|
29
|
+
* {children}
|
|
30
|
+
* </div>
|
|
31
|
+
* );
|
|
32
|
+
*
|
|
33
|
+
* const CustomTitle = ({ children }) => (
|
|
34
|
+
* <h2 className="modal-title">{children}</h2>
|
|
35
|
+
* );
|
|
36
|
+
*
|
|
37
|
+
* <ModalProvider
|
|
38
|
+
* ForegroundComponent={CustomForeground}
|
|
39
|
+
* TitleComponent={CustomTitle}
|
|
40
|
+
* options={{
|
|
41
|
+
* duration: 300,
|
|
42
|
+
* backdrop: 'rgba(0, 0, 0, 0.5)',
|
|
43
|
+
* closeOnBackdropClick: false,
|
|
44
|
+
* }}
|
|
45
|
+
* >
|
|
46
|
+
* <App />
|
|
47
|
+
* </ModalProvider>
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* Manual initialization with custom anchor:
|
|
52
|
+
* ```tsx
|
|
53
|
+
* function CustomModalContainer() {
|
|
54
|
+
* const modalRef = useRef<ModalProviderHandle>(null);
|
|
55
|
+
* const containerRef = useRef<HTMLDivElement>(null);
|
|
56
|
+
*
|
|
57
|
+
* useEffect(() => {
|
|
58
|
+
* if (containerRef.current) {
|
|
59
|
+
* modalRef.current?.initialize(containerRef.current);
|
|
60
|
+
* }
|
|
61
|
+
* }, []);
|
|
62
|
+
*
|
|
63
|
+
* return (
|
|
64
|
+
* <ModalProvider ref={modalRef}>
|
|
65
|
+
* <div className="app-layout">
|
|
66
|
+
* <main>Main Content</main>
|
|
67
|
+
* <div ref={containerRef} className="modal-container" />
|
|
68
|
+
* </div>
|
|
69
|
+
* </ModalProvider>
|
|
70
|
+
* );
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* With routing integration:
|
|
76
|
+
* ```tsx
|
|
77
|
+
* import { useLocation } from 'react-router-dom';
|
|
78
|
+
*
|
|
79
|
+
* function useRouterPathname() {
|
|
80
|
+
* const location = useLocation();
|
|
81
|
+
* return { pathname: location.pathname };
|
|
82
|
+
* }
|
|
83
|
+
*
|
|
84
|
+
* <ModalProvider usePathname={useRouterPathname}>
|
|
85
|
+
* <RouterApp />
|
|
86
|
+
* </ModalProvider>
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* Sharing context with modals:
|
|
91
|
+
* ```tsx
|
|
92
|
+
* const ThemeContext = createContext({ theme: 'light' });
|
|
93
|
+
*
|
|
94
|
+
* function ThemedModal() {
|
|
95
|
+
* const { theme } = useContext(ThemeContext);
|
|
96
|
+
* return <div className={`modal-${theme}`}>...</div>;
|
|
97
|
+
* }
|
|
98
|
+
*
|
|
99
|
+
* <ModalProvider
|
|
100
|
+
* context={{ userId: currentUser.id }}
|
|
101
|
+
* ContentComponent={({ children, context }) => (
|
|
102
|
+
* <ThemeContext.Provider value={{ theme: context.theme }}>
|
|
103
|
+
* {children}
|
|
104
|
+
* </ThemeContext.Provider>
|
|
105
|
+
* )}
|
|
106
|
+
* >
|
|
107
|
+
* <App />
|
|
108
|
+
* </ModalProvider>
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @remarks
|
|
112
|
+
* - Without a ref, the provider initializes automatically on mount
|
|
113
|
+
* - With a ref, you must call `initialize()` manually with target element
|
|
114
|
+
* - All modals created within this provider share the same configuration
|
|
115
|
+
* - The provider creates a portal for rendering modals outside the React tree
|
|
116
|
+
*/
|
|
2
117
|
export declare const BootstrapProvider: import("react").ForwardRefExoticComponent<{
|
|
3
118
|
usePathname?: import("../../@aileron/declare").Fn<[], {
|
|
4
119
|
pathname: string;
|
|
@@ -1,4 +1,186 @@
|
|
|
1
1
|
import type { BootstrapProviderProps } from './type';
|
|
2
|
+
/**
|
|
3
|
+
* Hook for bootstrapping the promise-modal system without a provider component.
|
|
4
|
+
*
|
|
5
|
+
* Provides the same functionality as BootstrapProvider but as a hook, allowing
|
|
6
|
+
* more flexible integration patterns. Returns a portal element and an initialize
|
|
7
|
+
* function for manual control.
|
|
8
|
+
*
|
|
9
|
+
* @param props - Configuration options (same as BootstrapProvider)
|
|
10
|
+
* @param props.mode - 'auto' for automatic initialization, 'manual' for explicit control
|
|
11
|
+
* @returns Object containing portal element and initialize function
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* Basic usage with automatic initialization:
|
|
15
|
+
* ```tsx
|
|
16
|
+
* function App() {
|
|
17
|
+
* const { portal } = useBootstrap({
|
|
18
|
+
* options: {
|
|
19
|
+
* duration: 200,
|
|
20
|
+
* backdrop: 'rgba(0, 0, 0, 0.8)',
|
|
21
|
+
* },
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* return (
|
|
25
|
+
* <>
|
|
26
|
+
* <button onClick={() => alert({ title: 'Hello!' })}>Alert</button>
|
|
27
|
+
* <button onClick={() => confirm({ title: 'Confirm?' })}>Confirm</button>
|
|
28
|
+
* {portal}
|
|
29
|
+
* </>
|
|
30
|
+
* );
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* Manual initialization with custom container:
|
|
36
|
+
* ```tsx
|
|
37
|
+
* function CustomModalApp() {
|
|
38
|
+
* const { portal, initialize } = useBootstrap({
|
|
39
|
+
* mode: 'manual',
|
|
40
|
+
* ForegroundComponent: CustomModal,
|
|
41
|
+
* options: {
|
|
42
|
+
* closeOnBackdropClick: false,
|
|
43
|
+
* },
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* useEffect(() => {
|
|
47
|
+
* const container = document.getElementById('modal-root');
|
|
48
|
+
* if (container) {
|
|
49
|
+
* initialize(container);
|
|
50
|
+
* }
|
|
51
|
+
* }, [initialize]);
|
|
52
|
+
*
|
|
53
|
+
* return (
|
|
54
|
+
* <div>
|
|
55
|
+
* <main>App Content</main>
|
|
56
|
+
* <div id="modal-root" />
|
|
57
|
+
* {portal}
|
|
58
|
+
* </div>
|
|
59
|
+
* );
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* Integration with state management:
|
|
65
|
+
* ```tsx
|
|
66
|
+
* function ConnectedApp() {
|
|
67
|
+
* const user = useSelector(selectCurrentUser);
|
|
68
|
+
* const theme = useSelector(selectTheme);
|
|
69
|
+
*
|
|
70
|
+
* const { portal } = useBootstrap({
|
|
71
|
+
* context: { userId: user?.id, theme },
|
|
72
|
+
* ContentComponent: ({ children, context }) => (
|
|
73
|
+
* <div data-theme={context.theme}>
|
|
74
|
+
* {children}
|
|
75
|
+
* </div>
|
|
76
|
+
* ),
|
|
77
|
+
* });
|
|
78
|
+
*
|
|
79
|
+
* const handleDelete = async () => {
|
|
80
|
+
* const confirmed = await confirm({
|
|
81
|
+
* title: 'Delete Account',
|
|
82
|
+
* content: `Are you sure, ${user.name}?`,
|
|
83
|
+
* });
|
|
84
|
+
* if (confirmed) {
|
|
85
|
+
* dispatch(deleteAccount());
|
|
86
|
+
* }
|
|
87
|
+
* };
|
|
88
|
+
*
|
|
89
|
+
* return (
|
|
90
|
+
* <>
|
|
91
|
+
* <button onClick={handleDelete}>Delete Account</button>
|
|
92
|
+
* {portal}
|
|
93
|
+
* </>
|
|
94
|
+
* );
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* Multiple modal systems:
|
|
100
|
+
* ```tsx
|
|
101
|
+
* function MultiModalApp() {
|
|
102
|
+
* // System modals (errors, confirmations)
|
|
103
|
+
* const { portal: systemPortal } = useBootstrap({
|
|
104
|
+
* ForegroundComponent: SystemModal,
|
|
105
|
+
* options: { backdrop: 'rgba(255, 0, 0, 0.8)' },
|
|
106
|
+
* });
|
|
107
|
+
*
|
|
108
|
+
* // Feature modals (forms, wizards)
|
|
109
|
+
* const { portal: featurePortal } = useBootstrap({
|
|
110
|
+
* ForegroundComponent: FeatureModal,
|
|
111
|
+
* options: { backdrop: 'rgba(0, 0, 255, 0.8)' },
|
|
112
|
+
* });
|
|
113
|
+
*
|
|
114
|
+
* return (
|
|
115
|
+
* <>
|
|
116
|
+
* <SystemSection />
|
|
117
|
+
* <FeatureSection />
|
|
118
|
+
* {systemPortal}
|
|
119
|
+
* {featurePortal}
|
|
120
|
+
* </>
|
|
121
|
+
* );
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* With custom hooks for specific modal types:
|
|
127
|
+
* ```tsx
|
|
128
|
+
* function useConfirmDialog() {
|
|
129
|
+
* const { portal } = useBootstrap({
|
|
130
|
+
* TitleComponent: ({ children }) => (
|
|
131
|
+
* <h3 className="confirm-title">
|
|
132
|
+
* <Icon name="warning" />
|
|
133
|
+
* {children}
|
|
134
|
+
* </h3>
|
|
135
|
+
* ),
|
|
136
|
+
* FooterComponent: ({ onConfirm, onClose }) => (
|
|
137
|
+
* <div className="confirm-footer">
|
|
138
|
+
* <button onClick={onClose}>Cancel</button>
|
|
139
|
+
* <button onClick={onConfirm} className="danger">
|
|
140
|
+
* Confirm
|
|
141
|
+
* </button>
|
|
142
|
+
* </div>
|
|
143
|
+
* ),
|
|
144
|
+
* });
|
|
145
|
+
*
|
|
146
|
+
* const confirmAction = useCallback(
|
|
147
|
+
* (title: string, content: string) => {
|
|
148
|
+
* return confirm({ title, content });
|
|
149
|
+
* },
|
|
150
|
+
* [],
|
|
151
|
+
* );
|
|
152
|
+
*
|
|
153
|
+
* return { portal, confirmAction };
|
|
154
|
+
* }
|
|
155
|
+
*
|
|
156
|
+
* function App() {
|
|
157
|
+
* const { portal, confirmAction } = useConfirmDialog();
|
|
158
|
+
*
|
|
159
|
+
* const handleDelete = async () => {
|
|
160
|
+
* const confirmed = await confirmAction(
|
|
161
|
+
* 'Delete Item',
|
|
162
|
+
* 'This action cannot be undone.',
|
|
163
|
+
* );
|
|
164
|
+
* if (confirmed) {
|
|
165
|
+
* // Perform deletion
|
|
166
|
+
* }
|
|
167
|
+
* };
|
|
168
|
+
*
|
|
169
|
+
* return (
|
|
170
|
+
* <>
|
|
171
|
+
* <button onClick={handleDelete}>Delete</button>
|
|
172
|
+
* {portal}
|
|
173
|
+
* </>
|
|
174
|
+
* );
|
|
175
|
+
* }
|
|
176
|
+
* ```
|
|
177
|
+
*
|
|
178
|
+
* @remarks
|
|
179
|
+
* - Use `mode: 'manual'` when you need to control the initialization timing
|
|
180
|
+
* - The portal element must be rendered in your component tree
|
|
181
|
+
* - Each hook instance creates an independent modal system
|
|
182
|
+
* - Context changes will affect all modals created after the change
|
|
183
|
+
*/
|
|
2
184
|
export declare const useBootstrap: ({ usePathname: useExternalPathname, ForegroundComponent, BackgroundComponent, TitleComponent, SubtitleComponent, ContentComponent, FooterComponent, options, context, mode, }?: BootstrapProviderProps & {
|
|
3
185
|
mode?: "manual" | "auto";
|
|
4
186
|
}) => {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { ComponentType, ReactNode } from 'react';
|
|
2
2
|
import type { AlertContentProps, AlertFooterRender, BackgroundComponent, FooterOptions, ForegroundComponent, ModalBackground } from '../../types';
|
|
3
|
-
interface AlertProps<
|
|
3
|
+
interface AlertProps<BackgroundValue> {
|
|
4
4
|
group?: string;
|
|
5
5
|
subtype?: 'info' | 'success' | 'warning' | 'error';
|
|
6
6
|
title?: ReactNode;
|
|
7
7
|
subtitle?: ReactNode;
|
|
8
8
|
content?: ReactNode | ComponentType<AlertContentProps>;
|
|
9
|
-
background?: ModalBackground<
|
|
9
|
+
background?: ModalBackground<BackgroundValue>;
|
|
10
10
|
footer?: AlertFooterRender | Pick<FooterOptions, 'confirm' | 'hideConfirm'> | false;
|
|
11
11
|
dimmed?: boolean;
|
|
12
12
|
manualDestroy?: boolean;
|
|
@@ -14,5 +14,146 @@ interface AlertProps<B> {
|
|
|
14
14
|
ForegroundComponent?: ForegroundComponent;
|
|
15
15
|
BackgroundComponent?: BackgroundComponent;
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Displays a promise-based alert modal that resolves when the user acknowledges.
|
|
19
|
+
*
|
|
20
|
+
* Creates a modal dialog with a single action button (typically "OK" or "Confirm").
|
|
21
|
+
* The promise resolves when the user clicks the button or closes the modal.
|
|
22
|
+
*
|
|
23
|
+
* @typeParam BackgroundValue - Type of background data passed to BackgroundComponent
|
|
24
|
+
* @param props - Alert configuration options
|
|
25
|
+
* @returns Promise that resolves when the alert is dismissed
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* Basic alert:
|
|
29
|
+
* ```tsx
|
|
30
|
+
* await alert({
|
|
31
|
+
* title: 'Success!',
|
|
32
|
+
* content: 'Your changes have been saved.',
|
|
33
|
+
* });
|
|
34
|
+
* console.log('Alert closed');
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* Alert with subtype styling:
|
|
39
|
+
* ```tsx
|
|
40
|
+
* // Error alert
|
|
41
|
+
* alert({
|
|
42
|
+
* subtype: 'error',
|
|
43
|
+
* title: 'Operation Failed',
|
|
44
|
+
* content: 'Unable to connect to server. Please try again later.',
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* // Success alert
|
|
48
|
+
* alert({
|
|
49
|
+
* subtype: 'success',
|
|
50
|
+
* title: 'Upload Complete',
|
|
51
|
+
* content: 'Your file has been successfully uploaded.',
|
|
52
|
+
* });
|
|
53
|
+
*
|
|
54
|
+
* // Warning alert
|
|
55
|
+
* alert({
|
|
56
|
+
* subtype: 'warning',
|
|
57
|
+
* title: 'Storage Almost Full',
|
|
58
|
+
* content: 'You have used 90% of your storage quota.',
|
|
59
|
+
* });
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* Custom content component:
|
|
64
|
+
* ```tsx
|
|
65
|
+
* const ErrorDetails = ({ onConfirm }) => (
|
|
66
|
+
* <div className="error-details">
|
|
67
|
+
* <p>Error Code: 500</p>
|
|
68
|
+
* <p>Time: {new Date().toLocaleString()}</p>
|
|
69
|
+
* <details>
|
|
70
|
+
* <summary>Stack Trace</summary>
|
|
71
|
+
* <pre>{error.stack}</pre>
|
|
72
|
+
* </details>
|
|
73
|
+
* </div>
|
|
74
|
+
* );
|
|
75
|
+
*
|
|
76
|
+
* alert({
|
|
77
|
+
* title: 'Application Error',
|
|
78
|
+
* content: ErrorDetails,
|
|
79
|
+
* subtype: 'error',
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* Custom footer:
|
|
85
|
+
* ```tsx
|
|
86
|
+
* alert({
|
|
87
|
+
* title: 'Terms Updated',
|
|
88
|
+
* content: 'Our terms of service have been updated.',
|
|
89
|
+
* footer: ({ onConfirm, context }) => (
|
|
90
|
+
* <div className="custom-footer">
|
|
91
|
+
* <a href="/terms" target="_blank">Read Terms</a>
|
|
92
|
+
* <button onClick={onConfirm}>I Understand</button>
|
|
93
|
+
* </div>
|
|
94
|
+
* ),
|
|
95
|
+
* });
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* Alert with background animation:
|
|
100
|
+
* ```tsx
|
|
101
|
+
* alert({
|
|
102
|
+
* title: 'Achievement Unlocked!',
|
|
103
|
+
* content: 'You completed your first task!',
|
|
104
|
+
* background: {
|
|
105
|
+
* data: { type: 'confetti', color: 'gold' },
|
|
106
|
+
* },
|
|
107
|
+
* BackgroundComponent: ({ background, visible }) => (
|
|
108
|
+
* <ConfettiAnimation
|
|
109
|
+
* active={visible}
|
|
110
|
+
* color={background.data.color}
|
|
111
|
+
* />
|
|
112
|
+
* ),
|
|
113
|
+
* });
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* Non-closeable critical alert:
|
|
118
|
+
* ```tsx
|
|
119
|
+
* alert({
|
|
120
|
+
* title: 'System Maintenance',
|
|
121
|
+
* content: 'The system will restart in 5 minutes. Please save your work.',
|
|
122
|
+
* closeOnBackdropClick: false,
|
|
123
|
+
* footer: {
|
|
124
|
+
* confirm: 'Got it',
|
|
125
|
+
* },
|
|
126
|
+
* manualDestroy: true,
|
|
127
|
+
* });
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* Chaining alerts:
|
|
132
|
+
* ```tsx
|
|
133
|
+
* async function showWelcomeTour() {
|
|
134
|
+
* await alert({
|
|
135
|
+
* title: 'Welcome!',
|
|
136
|
+
* content: 'Let\'s take a quick tour of the app.',
|
|
137
|
+
* });
|
|
138
|
+
*
|
|
139
|
+
* await alert({
|
|
140
|
+
* title: 'Dashboard',
|
|
141
|
+
* content: 'This is where you\'ll see your daily summary.',
|
|
142
|
+
* });
|
|
143
|
+
*
|
|
144
|
+
* await alert({
|
|
145
|
+
* title: 'Get Started',
|
|
146
|
+
* content: 'You\'re all set! Start exploring the app.',
|
|
147
|
+
* subtype: 'success',
|
|
148
|
+
* });
|
|
149
|
+
* }
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* @remarks
|
|
153
|
+
* - The promise always resolves (never rejects) unless an error occurs during modal creation
|
|
154
|
+
* - Use `manualDestroy: true` to keep the modal in DOM after closing (for animations)
|
|
155
|
+
* - Set `closeOnBackdropClick: false` to prevent closing by clicking outside
|
|
156
|
+
* - The `group` prop can be used to manage multiple related modals
|
|
157
|
+
*/
|
|
158
|
+
export declare const alert: <BackgroundValue = any>({ group, subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }: AlertProps<BackgroundValue>) => Promise<void>;
|
|
18
159
|
export {};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { ComponentType, ReactNode } from 'react';
|
|
2
2
|
import type { BackgroundComponent, ConfirmContentProps, ConfirmFooterRender, FooterOptions, ForegroundComponent, ModalBackground } from '../../types';
|
|
3
|
-
interface ConfirmProps<
|
|
3
|
+
interface ConfirmProps<BackgroundValue> {
|
|
4
4
|
group?: string;
|
|
5
5
|
subtype?: 'info' | 'success' | 'warning' | 'error';
|
|
6
6
|
title?: ReactNode;
|
|
7
7
|
subtitle?: ReactNode;
|
|
8
8
|
content?: ReactNode | ComponentType<ConfirmContentProps>;
|
|
9
|
-
background?: ModalBackground<
|
|
9
|
+
background?: ModalBackground<BackgroundValue>;
|
|
10
10
|
footer?: ConfirmFooterRender | FooterOptions | false;
|
|
11
11
|
dimmed?: boolean;
|
|
12
12
|
manualDestroy?: boolean;
|
|
@@ -14,5 +14,152 @@ interface ConfirmProps<B> {
|
|
|
14
14
|
ForegroundComponent?: ForegroundComponent;
|
|
15
15
|
BackgroundComponent?: BackgroundComponent;
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Displays a promise-based confirmation modal that resolves with a boolean result.
|
|
19
|
+
*
|
|
20
|
+
* Creates a modal dialog with two action buttons (typically "OK/Cancel" or "Yes/No").
|
|
21
|
+
* The promise resolves with `true` when confirmed, `false` when cancelled.
|
|
22
|
+
*
|
|
23
|
+
* @typeParam BackgroundValue - Type of background data passed to BackgroundComponent
|
|
24
|
+
* @param props - Confirmation dialog configuration options
|
|
25
|
+
* @returns Promise that resolves to true if confirmed, false if cancelled
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* Basic confirmation:
|
|
29
|
+
* ```tsx
|
|
30
|
+
* const shouldDelete = await confirm({
|
|
31
|
+
* title: 'Delete Item?',
|
|
32
|
+
* content: 'This action cannot be undone.',
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* if (shouldDelete) {
|
|
36
|
+
* await deleteItem();
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* Destructive action with warning:
|
|
42
|
+
* ```tsx
|
|
43
|
+
* const shouldProceed = await confirm({
|
|
44
|
+
* subtype: 'error',
|
|
45
|
+
* title: 'Delete Account',
|
|
46
|
+
* content: 'Are you sure you want to delete your account? All data will be permanently lost.',
|
|
47
|
+
* footer: {
|
|
48
|
+
* confirm: 'Delete Account',
|
|
49
|
+
* cancel: 'Keep Account',
|
|
50
|
+
* confirmDanger: true,
|
|
51
|
+
* },
|
|
52
|
+
* });
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* Custom content with details:
|
|
57
|
+
* ```tsx
|
|
58
|
+
* const ConfirmDetails = ({ onConfirm, onCancel }) => (
|
|
59
|
+
* <div>
|
|
60
|
+
* <p>The following items will be affected:</p>
|
|
61
|
+
* <ul>
|
|
62
|
+
* <li>15 documents</li>
|
|
63
|
+
* <li>42 images</li>
|
|
64
|
+
* <li>3 videos</li>
|
|
65
|
+
* </ul>
|
|
66
|
+
* <p>Total size: 1.2 GB</p>
|
|
67
|
+
* </div>
|
|
68
|
+
* );
|
|
69
|
+
*
|
|
70
|
+
* const confirmed = await confirm({
|
|
71
|
+
* title: 'Move to Trash?',
|
|
72
|
+
* content: ConfirmDetails,
|
|
73
|
+
* });
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* Custom footer with additional actions:
|
|
78
|
+
* ```tsx
|
|
79
|
+
* confirm({
|
|
80
|
+
* title: 'Save Changes?',
|
|
81
|
+
* content: 'You have unsaved changes.',
|
|
82
|
+
* footer: ({ onConfirm, onCancel, context }) => (
|
|
83
|
+
* <div className="save-dialog-footer">
|
|
84
|
+
* <button onClick={onCancel}>Don\'t Save</button>
|
|
85
|
+
* <button onClick={() => {
|
|
86
|
+
* saveAsDraft();
|
|
87
|
+
* onCancel();
|
|
88
|
+
* }}>Save as Draft</button>
|
|
89
|
+
* <button onClick={onConfirm} className="primary">
|
|
90
|
+
* Save and Publish
|
|
91
|
+
* </button>
|
|
92
|
+
* </div>
|
|
93
|
+
* ),
|
|
94
|
+
* });
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* Conditional confirmation flow:
|
|
99
|
+
* ```tsx
|
|
100
|
+
* async function deleteWithConfirmation(item) {
|
|
101
|
+
* // First confirmation
|
|
102
|
+
* const confirmDelete = await confirm({
|
|
103
|
+
* title: `Delete "${item.name}"?`,
|
|
104
|
+
* content: 'This item will be moved to trash.',
|
|
105
|
+
* });
|
|
106
|
+
*
|
|
107
|
+
* if (!confirmDelete) return;
|
|
108
|
+
*
|
|
109
|
+
* // Check if item has dependencies
|
|
110
|
+
* if (item.dependencies.length > 0) {
|
|
111
|
+
* const confirmForce = await confirm({
|
|
112
|
+
* subtype: 'warning',
|
|
113
|
+
* title: 'Item has dependencies',
|
|
114
|
+
* content: `${item.dependencies.length} other items depend on this. Delete anyway?`,
|
|
115
|
+
* footer: {
|
|
116
|
+
* confirm: 'Force Delete',
|
|
117
|
+
* confirmDanger: true,
|
|
118
|
+
* },
|
|
119
|
+
* });
|
|
120
|
+
*
|
|
121
|
+
* if (!confirmForce) return;
|
|
122
|
+
* }
|
|
123
|
+
*
|
|
124
|
+
* await deleteItem(item.id);
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* With loading state:
|
|
130
|
+
* ```tsx
|
|
131
|
+
* const shouldExport = await confirm({
|
|
132
|
+
* title: 'Export Data',
|
|
133
|
+
* content: 'Export may take several minutes for large datasets.',
|
|
134
|
+
* footer: ({ onConfirm, onCancel }) => {
|
|
135
|
+
* const [loading, setLoading] = useState(false);
|
|
136
|
+
*
|
|
137
|
+
* const handleExport = async () => {
|
|
138
|
+
* setLoading(true);
|
|
139
|
+
* await startExport();
|
|
140
|
+
* onConfirm();
|
|
141
|
+
* };
|
|
142
|
+
*
|
|
143
|
+
* return (
|
|
144
|
+
* <>
|
|
145
|
+
* <button onClick={onCancel}>Cancel</button>
|
|
146
|
+
* <button
|
|
147
|
+
* onClick={handleExport}
|
|
148
|
+
* disabled={loading}
|
|
149
|
+
* >
|
|
150
|
+
* {loading ? 'Exporting...' : 'Start Export'}
|
|
151
|
+
* </button>
|
|
152
|
+
* </>
|
|
153
|
+
* );
|
|
154
|
+
* },
|
|
155
|
+
* });
|
|
156
|
+
* ```
|
|
157
|
+
*
|
|
158
|
+
* @remarks
|
|
159
|
+
* - Returns `true` only when explicitly confirmed via the confirm action
|
|
160
|
+
* - Returns `false` for cancel action or backdrop click (if enabled)
|
|
161
|
+
* - Use `subtype` to indicate severity (error for destructive actions)
|
|
162
|
+
* - The `footer` prop allows complete customization of button layout and behavior
|
|
163
|
+
*/
|
|
164
|
+
export declare const confirm: <BackgroundValue = any>({ group, subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }: ConfirmProps<BackgroundValue>) => Promise<boolean>;
|
|
18
165
|
export {};
|