@lerx/promise-modal 0.10.4 → 0.11.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/docs/claude/commands/guide.md +760 -0
- package/docs/claude/skills/expert/SKILL.md +171 -0
- package/docs/claude/skills/expert/knowledge/advanced-patterns.md +294 -0
- package/docs/claude/skills/expert/knowledge/api-reference.md +175 -0
- package/docs/claude/skills/expert/knowledge/hooks-reference.md +207 -0
- package/docs/claude/skills/expert/knowledge/type-definitions.md +172 -0
- package/docs/en/SPECIFICATION.md +1185 -0
- package/docs/ko/SPECIFICATION.md +1193 -0
- package/package.json +5 -4
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: promise-modal-expert
|
|
3
|
+
description: "@lerx/promise-modal library expert. Guide users on React-based Promise modal utilities (alert, confirm, prompt), architecture, and customization."
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Promise Modal Expert Skill
|
|
8
|
+
|
|
9
|
+
## Role
|
|
10
|
+
|
|
11
|
+
You are an expert on the `@lerx/promise-modal` library. Help users effectively implement modal dialogs using React-based Promise modal utilities.
|
|
12
|
+
|
|
13
|
+
## Core Knowledge
|
|
14
|
+
|
|
15
|
+
### Library Overview
|
|
16
|
+
|
|
17
|
+
`@lerx/promise-modal` is a universal modal utility for React that provides:
|
|
18
|
+
- Promise-based modal interactions (alert, confirm, prompt)
|
|
19
|
+
- Usage both inside and outside React components
|
|
20
|
+
- High-level component customization
|
|
21
|
+
- Automatic lifecycle management
|
|
22
|
+
- Programmatic modal cancellation via AbortSignal
|
|
23
|
+
|
|
24
|
+
### Architecture
|
|
25
|
+
|
|
26
|
+
The library uses a layered architecture:
|
|
27
|
+
|
|
28
|
+
1. **Core Layer** - Main API functions (alert, confirm, prompt)
|
|
29
|
+
2. **Application Layer** - ModalManager singleton for lifecycle and DOM management
|
|
30
|
+
3. **Bootstrap Layer** - ModalProvider component for initialization
|
|
31
|
+
4. **Provider Layer** - Context providers for configuration and state
|
|
32
|
+
5. **Component Layer** - Customizable UI components
|
|
33
|
+
|
|
34
|
+
### Configuration Priority
|
|
35
|
+
|
|
36
|
+
Configuration is applied hierarchically, with lower levels overriding higher levels:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
Provider Config (Lowest) < Hook Config < Handler Config (Highest)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
| Level | Location | Description |
|
|
43
|
+
|------|------|------|
|
|
44
|
+
| **Provider** | `ModalProvider` props | App-wide default configuration |
|
|
45
|
+
| **Hook** | `useModal(config)` | Component-level configuration |
|
|
46
|
+
| **Handler** | `alert/confirm/prompt(options)` | Individual modal configuration |
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Basic Usage Patterns
|
|
51
|
+
|
|
52
|
+
### Pattern 1: Basic Static API
|
|
53
|
+
|
|
54
|
+
Static functions usable outside React components.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { alert, confirm, prompt } from '@lerx/promise-modal';
|
|
58
|
+
|
|
59
|
+
// Simple alert
|
|
60
|
+
await alert({
|
|
61
|
+
title: 'Alert',
|
|
62
|
+
content: 'Task completed successfully.',
|
|
63
|
+
subtype: 'success'
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Confirmation modal
|
|
67
|
+
if (await confirm({
|
|
68
|
+
title: 'Confirm',
|
|
69
|
+
content: 'Are you sure you want to delete this?'
|
|
70
|
+
})) {
|
|
71
|
+
// User confirmed
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Prompt input
|
|
75
|
+
const name = await prompt({
|
|
76
|
+
title: 'Enter Name',
|
|
77
|
+
defaultValue: '',
|
|
78
|
+
Input: ({ value, onChange }) => (
|
|
79
|
+
<input
|
|
80
|
+
value={value}
|
|
81
|
+
onChange={(e) => onChange(e.target.value)}
|
|
82
|
+
placeholder="Enter your name"
|
|
83
|
+
/>
|
|
84
|
+
),
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Pattern 2: Component-Scoped with useModal
|
|
89
|
+
|
|
90
|
+
Tied to component lifecycle, automatically cleaned up on unmount.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { useModal } from '@lerx/promise-modal';
|
|
94
|
+
|
|
95
|
+
function MyComponent() {
|
|
96
|
+
const modal = useModal({
|
|
97
|
+
ForegroundComponent: CustomForeground, // Hook-level config
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const handleDelete = async () => {
|
|
101
|
+
if (await modal.confirm({ content: 'Delete this item?' })) {
|
|
102
|
+
// Delete logic
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
return <button onClick={handleDelete}>Delete</button>;
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Key Differences**:
|
|
111
|
+
|
|
112
|
+
| Feature | Static Handlers | useModal Hook |
|
|
113
|
+
|------|------------|-------------|
|
|
114
|
+
| Lifecycle | Independent | Tied to component |
|
|
115
|
+
| Cleanup | Manual | Auto on unmount |
|
|
116
|
+
| Usage Location | Anywhere | Inside React components |
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Troubleshooting
|
|
121
|
+
|
|
122
|
+
### Modal Not Appearing
|
|
123
|
+
|
|
124
|
+
1. Verify `ModalProvider` is at app root
|
|
125
|
+
2. (For manual mode) Ensure `initialize()` was called
|
|
126
|
+
3. Check for z-index and CSS conflicts
|
|
127
|
+
|
|
128
|
+
### Modal Not Closing
|
|
129
|
+
|
|
130
|
+
1. Check `manualDestroy` option
|
|
131
|
+
2. Verify `closeOnBackdropClick` configuration
|
|
132
|
+
3. Ensure `onClose` or `onConfirm` is being called
|
|
133
|
+
|
|
134
|
+
### Animation Not Working
|
|
135
|
+
|
|
136
|
+
1. Verify `useModalAnimation` hook is used correctly
|
|
137
|
+
2. Check CSS transition properties
|
|
138
|
+
3. Confirm Provider's `duration` option
|
|
139
|
+
|
|
140
|
+
### prompt Type Errors
|
|
141
|
+
|
|
142
|
+
1. Specify generic type: `prompt<string>(...)`
|
|
143
|
+
2. Ensure `defaultValue` matches type
|
|
144
|
+
3. Check `onChange` handler type
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Best Practices
|
|
149
|
+
|
|
150
|
+
1. **Place ModalProvider at Root**: Wrap entire app
|
|
151
|
+
2. **Use useModal in Components**: For automatic cleanup
|
|
152
|
+
3. **Use Static API in Utilities**: For non-component code
|
|
153
|
+
4. **Customize at Provider Level**: Set global styles once
|
|
154
|
+
5. **Use subtype Semantically**: info, success, warning, error
|
|
155
|
+
6. **Handle Promises Properly**: Always await or handle rejection
|
|
156
|
+
7. **Keep Modal Content Simple**: Avoid complex state in modals
|
|
157
|
+
8. **Test Accessibility**: Verify keyboard navigation
|
|
158
|
+
9. **Leverage AbortSignal**: When programmatic modal closure is needed
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Knowledge Files Reference
|
|
163
|
+
|
|
164
|
+
For detailed API, advanced patterns, and type definitions, refer to these knowledge files:
|
|
165
|
+
|
|
166
|
+
| File | Contents |
|
|
167
|
+
|------|------|
|
|
168
|
+
| `knowledge/api-reference.md` | Static functions (alert, confirm, prompt) and ModalProvider detailed API |
|
|
169
|
+
| `knowledge/hooks-reference.md` | Complete reference for 8 hooks (useModal, useActiveModalCount, etc.) |
|
|
170
|
+
| `knowledge/advanced-patterns.md` | Advanced patterns including AbortSignal, toast, nested modals, custom anchors |
|
|
171
|
+
| `knowledge/type-definitions.md` | TypeScript interface definitions (ModalFrameProps, etc.) |
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Advanced Patterns
|
|
2
|
+
|
|
3
|
+
## Pattern 3: Modal Cancellation with AbortSignal
|
|
4
|
+
|
|
5
|
+
Programmatically cancel modals.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { alert } from '@lerx/promise-modal';
|
|
9
|
+
import { useState } from 'react';
|
|
10
|
+
|
|
11
|
+
function ManualAbortControl() {
|
|
12
|
+
const [controller, setController] = useState<AbortController | null>(null);
|
|
13
|
+
|
|
14
|
+
const handleOpen = () => {
|
|
15
|
+
const newController = new AbortController();
|
|
16
|
+
setController(newController);
|
|
17
|
+
|
|
18
|
+
alert({
|
|
19
|
+
title: 'Manually Cancelable',
|
|
20
|
+
content: 'Click "Cancel" button to close this modal.',
|
|
21
|
+
signal: newController.signal,
|
|
22
|
+
closeOnBackdropClick: false,
|
|
23
|
+
}).then(() => {
|
|
24
|
+
setController(null);
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const handleAbort = () => {
|
|
29
|
+
if (controller) {
|
|
30
|
+
controller.abort();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div>
|
|
36
|
+
<button onClick={handleOpen} disabled={!!controller}>Open Modal</button>
|
|
37
|
+
<button onClick={handleAbort} disabled={!controller}>Cancel Modal</button>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Use Cases**:
|
|
44
|
+
- Timer-based auto-close
|
|
45
|
+
- Close modals in response to external events
|
|
46
|
+
- Programmatic modal control
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Pattern 4: Toast Implementation
|
|
51
|
+
|
|
52
|
+
Implement auto-dismissing toast notifications.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { alert, useModalAnimation, useModalDuration, useDestroyAfter } from '@lerx/promise-modal';
|
|
56
|
+
import { useRef, useEffect } from 'react';
|
|
57
|
+
|
|
58
|
+
const ToastForeground = ({ id, visible, children, onClose, onDestroy }) => {
|
|
59
|
+
const ref = useRef(null);
|
|
60
|
+
const { duration } = useModalDuration();
|
|
61
|
+
|
|
62
|
+
// Auto-close after 3 seconds
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const timer = setTimeout(onClose, 3000);
|
|
65
|
+
return () => clearTimeout(timer);
|
|
66
|
+
}, [onClose]);
|
|
67
|
+
|
|
68
|
+
// Handle animations
|
|
69
|
+
useModalAnimation(visible, {
|
|
70
|
+
onVisible: () => ref.current?.classList.add('visible'),
|
|
71
|
+
onHidden: () => ref.current?.classList.remove('visible'),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Remove from DOM after animation
|
|
75
|
+
useDestroyAfter(id, duration);
|
|
76
|
+
|
|
77
|
+
return <div ref={ref}>{children}</div>;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Pattern for removing previous toast
|
|
81
|
+
let onDestroyPrevToast: () => void;
|
|
82
|
+
|
|
83
|
+
export const toast = (message: ReactNode, duration = 1250) => {
|
|
84
|
+
onDestroyPrevToast?.(); // Remove previous toast
|
|
85
|
+
|
|
86
|
+
return alert({
|
|
87
|
+
content: message,
|
|
88
|
+
ForegroundComponent: (props) => {
|
|
89
|
+
onDestroyPrevToast = props.onDestroy;
|
|
90
|
+
return <ToastForeground {...props} />;
|
|
91
|
+
},
|
|
92
|
+
footer: false,
|
|
93
|
+
dimmed: false,
|
|
94
|
+
closeOnBackdropClick: false,
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Usage Example**:
|
|
100
|
+
```typescript
|
|
101
|
+
toast('Task completed successfully!');
|
|
102
|
+
toast('An error occurred.', 2000);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Pattern 5: Nested Modals
|
|
108
|
+
|
|
109
|
+
Display multiple modals sequentially in steps.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { alert, confirm, prompt } from '@lerx/promise-modal';
|
|
113
|
+
|
|
114
|
+
async function multiStepProcess() {
|
|
115
|
+
// Step 1: Start confirmation
|
|
116
|
+
if (!await confirm({
|
|
117
|
+
title: 'Start?',
|
|
118
|
+
content: 'Do you want to continue?'
|
|
119
|
+
})) return;
|
|
120
|
+
|
|
121
|
+
// Step 2: Collect user info
|
|
122
|
+
const name = await prompt({
|
|
123
|
+
title: 'Name',
|
|
124
|
+
defaultValue: '',
|
|
125
|
+
Input: ({ value, onChange }) => (
|
|
126
|
+
<input
|
|
127
|
+
value={value}
|
|
128
|
+
onChange={(e) => onChange(e.target.value)}
|
|
129
|
+
placeholder="Enter your name"
|
|
130
|
+
/>
|
|
131
|
+
),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (!name) return;
|
|
135
|
+
|
|
136
|
+
// Step 3: Completion notification
|
|
137
|
+
await alert({
|
|
138
|
+
title: 'Complete',
|
|
139
|
+
content: `Hello, ${name}!`,
|
|
140
|
+
subtype: 'success',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Notes**:
|
|
146
|
+
- Each modal opens after the previous one closes
|
|
147
|
+
- Natural flow through Promise chaining
|
|
148
|
+
- Entire process stops if user cancels
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Pattern 6: Custom Anchor
|
|
153
|
+
|
|
154
|
+
Render modals inside a specific DOM element.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { useInitializeModal } from '@lerx/promise-modal';
|
|
158
|
+
import { useRef, useEffect } from 'react';
|
|
159
|
+
|
|
160
|
+
function CustomAnchorExample() {
|
|
161
|
+
const { initialize, portal } = useInitializeModal({ mode: 'manual' });
|
|
162
|
+
const containerRef = useRef(null);
|
|
163
|
+
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (containerRef.current) {
|
|
166
|
+
initialize(containerRef.current);
|
|
167
|
+
}
|
|
168
|
+
}, [initialize]);
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<div>
|
|
172
|
+
<h1>Custom Modal Container</h1>
|
|
173
|
+
<div
|
|
174
|
+
ref={containerRef}
|
|
175
|
+
style={{
|
|
176
|
+
position: 'relative',
|
|
177
|
+
height: 500,
|
|
178
|
+
border: '2px solid #ccc',
|
|
179
|
+
}}
|
|
180
|
+
/>
|
|
181
|
+
{portal}
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Use Cases**:
|
|
188
|
+
- Restrict modals to specific area
|
|
189
|
+
- Manage multiple modal containers
|
|
190
|
+
- Use modals inside iframe or Shadow DOM
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Pattern 7: Complex Form Input
|
|
195
|
+
|
|
196
|
+
Collect complex form data using prompt.
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import { prompt } from '@lerx/promise-modal';
|
|
200
|
+
|
|
201
|
+
interface UserForm {
|
|
202
|
+
name: string;
|
|
203
|
+
email: string;
|
|
204
|
+
age: number;
|
|
205
|
+
agree: boolean;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function collectUserInfo() {
|
|
209
|
+
const userInfo = await prompt<UserForm>({
|
|
210
|
+
title: 'Enter User Information',
|
|
211
|
+
defaultValue: {
|
|
212
|
+
name: '',
|
|
213
|
+
email: '',
|
|
214
|
+
age: 18,
|
|
215
|
+
agree: false,
|
|
216
|
+
},
|
|
217
|
+
Input: ({ value, onChange }) => (
|
|
218
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
|
219
|
+
<input
|
|
220
|
+
type="text"
|
|
221
|
+
placeholder="Name"
|
|
222
|
+
value={value.name}
|
|
223
|
+
onChange={(e) => onChange({ ...value, name: e.target.value })}
|
|
224
|
+
/>
|
|
225
|
+
<input
|
|
226
|
+
type="email"
|
|
227
|
+
placeholder="Email"
|
|
228
|
+
value={value.email}
|
|
229
|
+
onChange={(e) => onChange({ ...value, email: e.target.value })}
|
|
230
|
+
/>
|
|
231
|
+
<input
|
|
232
|
+
type="number"
|
|
233
|
+
placeholder="Age"
|
|
234
|
+
value={value.age}
|
|
235
|
+
onChange={(e) => onChange({ ...value, age: Number(e.target.value) })}
|
|
236
|
+
/>
|
|
237
|
+
<label>
|
|
238
|
+
<input
|
|
239
|
+
type="checkbox"
|
|
240
|
+
checked={value.agree}
|
|
241
|
+
onChange={(e) => onChange({ ...value, agree: e.target.checked })}
|
|
242
|
+
/>
|
|
243
|
+
I agree to the terms
|
|
244
|
+
</label>
|
|
245
|
+
</div>
|
|
246
|
+
),
|
|
247
|
+
disabled: (value) => !value.name || !value.email || !value.agree,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
console.log('Collected info:', userInfo);
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Key Points**:
|
|
255
|
+
- Type-safe handling of complex objects
|
|
256
|
+
- Validation with `disabled` function
|
|
257
|
+
- Confirm button disabled until all fields are filled
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Pattern 8: Conditional Modal Styles
|
|
262
|
+
|
|
263
|
+
Display different styled modals based on situation.
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
import { alert } from '@lerx/promise-modal';
|
|
267
|
+
|
|
268
|
+
type NotificationType = 'info' | 'success' | 'warning' | 'error';
|
|
269
|
+
|
|
270
|
+
function notify(type: NotificationType, message: string) {
|
|
271
|
+
return alert({
|
|
272
|
+
title: {
|
|
273
|
+
info: 'Information',
|
|
274
|
+
success: 'Success',
|
|
275
|
+
warning: 'Warning',
|
|
276
|
+
error: 'Error',
|
|
277
|
+
}[type],
|
|
278
|
+
content: message,
|
|
279
|
+
subtype: type,
|
|
280
|
+
footer: {
|
|
281
|
+
confirm: 'OK',
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Usage examples
|
|
287
|
+
notify('success', 'Data saved successfully.');
|
|
288
|
+
notify('error', 'Network error occurred.');
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Advantages**:
|
|
292
|
+
- Consistent notification interface
|
|
293
|
+
- Automatic styling by type
|
|
294
|
+
- Reusable utility function
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
## Static Functions
|
|
4
|
+
|
|
5
|
+
### `alert(options)`
|
|
6
|
+
|
|
7
|
+
Opens a simple notification modal.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { alert } from '@lerx/promise-modal';
|
|
11
|
+
|
|
12
|
+
await alert({
|
|
13
|
+
title: 'Alert',
|
|
14
|
+
content: 'Task completed successfully.',
|
|
15
|
+
subtype: 'success', // 'info' | 'success' | 'warning' | 'error'
|
|
16
|
+
dimmed: true,
|
|
17
|
+
closeOnBackdropClick: true,
|
|
18
|
+
manualDestroy: false,
|
|
19
|
+
signal: abortController.signal, // Optional AbortSignal
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Options**:
|
|
24
|
+
|
|
25
|
+
| Option | Type | Description |
|
|
26
|
+
|------|------|------|
|
|
27
|
+
| `title` | `ReactNode` | Modal title |
|
|
28
|
+
| `subtitle` | `ReactNode` | Subtitle below title |
|
|
29
|
+
| `content` | `ReactNode \| ComponentType` | Modal content |
|
|
30
|
+
| `subtype` | `'info' \| 'success' \| 'warning' \| 'error'` | Modal styling type |
|
|
31
|
+
| `dimmed` | `boolean` | Whether to darken background |
|
|
32
|
+
| `closeOnBackdropClick` | `boolean` | Close on backdrop click |
|
|
33
|
+
| `manualDestroy` | `boolean` | Manual destruction mode |
|
|
34
|
+
| `duration` | `number \| string` | Animation duration |
|
|
35
|
+
| `background` | `ModalBackground` | Background configuration |
|
|
36
|
+
| `footer` | `Function \| Object \| false` | Footer configuration |
|
|
37
|
+
| `ForegroundComponent` | `ComponentType` | Custom foreground component |
|
|
38
|
+
| `BackgroundComponent` | `ComponentType` | Custom background component |
|
|
39
|
+
| `signal` | `AbortSignal` | AbortSignal for modal cancellation |
|
|
40
|
+
|
|
41
|
+
**Return Value**: `Promise<void>`
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
### `confirm(options)`
|
|
46
|
+
|
|
47
|
+
Opens a confirmation modal for user decision.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { confirm } from '@lerx/promise-modal';
|
|
51
|
+
|
|
52
|
+
const result = await confirm({
|
|
53
|
+
title: 'Confirm',
|
|
54
|
+
content: 'Are you sure you want to delete this?',
|
|
55
|
+
footer: {
|
|
56
|
+
confirm: 'Delete',
|
|
57
|
+
cancel: 'Cancel',
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (result) {
|
|
62
|
+
console.log('User confirmed');
|
|
63
|
+
} else {
|
|
64
|
+
console.log('User cancelled');
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Options**: Same options available as `alert()`
|
|
69
|
+
|
|
70
|
+
**Return Value**: `Promise<boolean>` - `true` if confirmed, `false` if cancelled
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### `prompt<T>(options)`
|
|
75
|
+
|
|
76
|
+
Opens a prompt modal to collect user input.
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { prompt } from '@lerx/promise-modal';
|
|
80
|
+
|
|
81
|
+
// Simple text input
|
|
82
|
+
const name = await prompt<string>({
|
|
83
|
+
title: 'Enter Name',
|
|
84
|
+
defaultValue: '',
|
|
85
|
+
Input: ({ value, onChange }) => (
|
|
86
|
+
<input
|
|
87
|
+
value={value}
|
|
88
|
+
onChange={(e) => onChange(e.target.value)}
|
|
89
|
+
placeholder="Enter your name"
|
|
90
|
+
/>
|
|
91
|
+
),
|
|
92
|
+
disabled: (value) => value.length < 2,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Complex object input
|
|
96
|
+
const userInfo = await prompt<{ name: string; age: number }>({
|
|
97
|
+
title: 'User Information',
|
|
98
|
+
defaultValue: { name: '', age: 0 },
|
|
99
|
+
Input: ({ value, onChange }) => (
|
|
100
|
+
<div>
|
|
101
|
+
<input
|
|
102
|
+
value={value.name}
|
|
103
|
+
onChange={(e) => onChange({ ...value, name: e.target.value })}
|
|
104
|
+
/>
|
|
105
|
+
<input
|
|
106
|
+
type="number"
|
|
107
|
+
value={value.age}
|
|
108
|
+
onChange={(e) => onChange({ ...value, age: Number(e.target.value) })}
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
111
|
+
),
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Additional Options**:
|
|
116
|
+
|
|
117
|
+
| Option | Type | Description |
|
|
118
|
+
|------|------|------|
|
|
119
|
+
| `Input` | `(props: PromptInputProps<T>) => ReactNode` | Input component (required) |
|
|
120
|
+
| `defaultValue` | `T` | Default input value |
|
|
121
|
+
| `disabled` | `(value: T) => boolean` | Condition to disable confirm button |
|
|
122
|
+
| `returnOnCancel` | `boolean` | Whether to return default value on cancel |
|
|
123
|
+
|
|
124
|
+
**Return Value**: `Promise<T>`
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## ModalProvider
|
|
129
|
+
|
|
130
|
+
Main Provider component for initialization.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { ModalProvider } from '@lerx/promise-modal';
|
|
134
|
+
import { useLocation } from 'react-router-dom';
|
|
135
|
+
|
|
136
|
+
function App() {
|
|
137
|
+
return (
|
|
138
|
+
<ModalProvider
|
|
139
|
+
usePathname={useLocation} // Router integration (auto-close on path change)
|
|
140
|
+
ForegroundComponent={CustomForeground}
|
|
141
|
+
BackgroundComponent={CustomBackground}
|
|
142
|
+
TitleComponent={CustomTitle}
|
|
143
|
+
SubtitleComponent={CustomSubtitle}
|
|
144
|
+
ContentComponent={CustomContent}
|
|
145
|
+
FooterComponent={CustomFooter}
|
|
146
|
+
options={{
|
|
147
|
+
duration: '200ms',
|
|
148
|
+
backdrop: 'rgba(0, 0, 0, 0.35)',
|
|
149
|
+
manualDestroy: true,
|
|
150
|
+
closeOnBackdropClick: true,
|
|
151
|
+
}}
|
|
152
|
+
context={{
|
|
153
|
+
theme: 'light',
|
|
154
|
+
locale: 'en-US',
|
|
155
|
+
}}
|
|
156
|
+
>
|
|
157
|
+
<YourApp />
|
|
158
|
+
</ModalProvider>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Props**:
|
|
164
|
+
|
|
165
|
+
| Prop | Type | Description |
|
|
166
|
+
|------|------|------|
|
|
167
|
+
| `usePathname` | `() => { pathname: string }` | Router path hook (closes modals on path change) |
|
|
168
|
+
| `ForegroundComponent` | `ComponentType<ModalFrameProps>` | Custom foreground component |
|
|
169
|
+
| `BackgroundComponent` | `ComponentType<ModalFrameProps>` | Custom background component |
|
|
170
|
+
| `TitleComponent` | `ComponentType` | Custom title component |
|
|
171
|
+
| `SubtitleComponent` | `ComponentType` | Custom subtitle component |
|
|
172
|
+
| `ContentComponent` | `ComponentType` | Custom content component |
|
|
173
|
+
| `FooterComponent` | `ComponentType<FooterComponentProps>` | Custom footer component |
|
|
174
|
+
| `options` | `ModalOptions` | Global modal options |
|
|
175
|
+
| `context` | `any` | User-defined context data |
|