@lerx/promise-modal 0.11.1 → 0.12.1

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.
@@ -0,0 +1,18 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "package": {
4
+ "name": "@lerx/promise-modal",
5
+ "version": "0.12.1"
6
+ },
7
+ "generatedAt": "2026-04-24T14:52:39.411Z",
8
+ "algorithm": "sha256",
9
+ "assetRoot": "docs/claude",
10
+ "files": {
11
+ "skills/promise-modal-skill/SKILL.md": "b424c979b9e969e11ba8fa0a56afc3cbf1793c822f06509e6b730068f384679e",
12
+ "skills/promise-modal-skill/knowledge/advanced-patterns.md": "2e2c29449a46b1336727f8a1d5f1eede4e99639675dbe819edd2bf4243acfaab",
13
+ "skills/promise-modal-skill/knowledge/api-reference.md": "1df36fa5b832d2349c05dacaec913876f7c30fad6478fcae64c8a4bd65bc023a",
14
+ "skills/promise-modal-skill/knowledge/hooks-reference.md": "b0137caf257ad9712cfa7ab44c908041bbbc61bf355fa0180925a35479e47516",
15
+ "skills/promise-modal-skill/knowledge/type-definitions.md": "fde17b7f99d3e65b7e6c16abf4689af0a9e41a8a06dfeb9482b94a7611841d50"
16
+ },
17
+ "previousVersions": {}
18
+ }
@@ -1,37 +1,33 @@
1
1
  ---
2
- name: promise-modal-expert
2
+ name: promise-modal-skill
3
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
4
  ---
6
5
 
7
- # Promise Modal Expert Skill
6
+ # Promise Modal Expert
8
7
 
9
- ## Role
8
+ Guide users on the `@lerx/promise-modal` library for React-based Promise modal utilities. Apply this skill when users ask how to implement, configure, customize, or troubleshoot modal dialogs built with `@lerx/promise-modal`.
10
9
 
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
10
+ ## Library Overview
16
11
 
17
12
  `@lerx/promise-modal` is a universal modal utility for React that provides:
13
+
18
14
  - Promise-based modal interactions (alert, confirm, prompt)
19
15
  - Usage both inside and outside React components
20
16
  - High-level component customization
21
17
  - Automatic lifecycle management
22
18
  - Programmatic modal cancellation via AbortSignal
23
19
 
24
- ### Architecture
20
+ ## Architecture
25
21
 
26
22
  The library uses a layered architecture:
27
23
 
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
24
+ 1. **Core Layer** Main API functions (alert, confirm, prompt)
25
+ 2. **Application Layer** ModalManager singleton for lifecycle and DOM management
26
+ 3. **Bootstrap Layer** ModalProvider component for initialization
27
+ 4. **Provider Layer** Context providers for configuration and state
28
+ 5. **Component Layer** Customizable UI components
33
29
 
34
- ### Configuration Priority
30
+ ## Configuration Priority
35
31
 
36
32
  Configuration is applied hierarchically, with lower levels overriding higher levels:
37
33
 
@@ -49,9 +45,9 @@ Provider Config (Lowest) < Hook Config < Handler Config (Highest)
49
45
 
50
46
  ## Basic Usage Patterns
51
47
 
52
- ### Pattern 1: Basic Static API
48
+ ### Pattern A: Static API
53
49
 
54
- Static functions usable outside React components.
50
+ Use static functions outside React components.
55
51
 
56
52
  ```typescript
57
53
  import { alert, confirm, prompt } from '@lerx/promise-modal';
@@ -85,9 +81,9 @@ const name = await prompt({
85
81
  });
86
82
  ```
87
83
 
88
- ### Pattern 2: Component-Scoped with useModal
84
+ ### Pattern B: Component-Scoped with useModal
89
85
 
90
- Tied to component lifecycle, automatically cleaned up on unmount.
86
+ Tie modals to component lifecycle automatically cleaned up on unmount.
91
87
 
92
88
  ```typescript
93
89
  import { useModal } from '@lerx/promise-modal';
@@ -107,7 +103,7 @@ function MyComponent() {
107
103
  }
108
104
  ```
109
105
 
110
- **Key Differences**:
106
+ **Static API vs useModal**:
111
107
 
112
108
  | Feature | Static Handlers | useModal Hook |
113
109
  |------|------------|-------------|
@@ -115,6 +111,8 @@ function MyComponent() {
115
111
  | Cleanup | Manual | Auto on unmount |
116
112
  | Usage Location | Anywhere | Inside React components |
117
113
 
114
+ For advanced patterns (AbortSignal cancellation, toasts, nested modals, custom anchors, complex forms), see `knowledge/advanced-patterns.md`.
115
+
118
116
  ---
119
117
 
120
118
  ## Troubleshooting
@@ -147,25 +145,25 @@ function MyComponent() {
147
145
 
148
146
  ## Best Practices
149
147
 
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
148
+ 1. **Place ModalProvider at Root** wrap the entire app
149
+ 2. **Use useModal in Components** for automatic cleanup
150
+ 3. **Use Static API in Utilities** for non-component code
151
+ 4. **Customize at Provider Level** set global styles once
152
+ 5. **Use subtype Semantically** info, success, warning, error
153
+ 6. **Handle Promises Properly** always await or handle rejection
154
+ 7. **Keep Modal Content Simple** avoid complex state in modals
155
+ 8. **Test Accessibility** verify keyboard navigation
156
+ 9. **Leverage AbortSignal** when programmatic modal closure is needed
159
157
 
160
158
  ---
161
159
 
162
- ## Knowledge Files Reference
160
+ ## Resources
163
161
 
164
- For detailed API, advanced patterns, and type definitions, refer to these knowledge files:
162
+ Load these files on demand for detailed API, patterns, and type definitions:
165
163
 
166
164
  | File | Contents |
167
165
  |------|------|
168
166
  | `knowledge/api-reference.md` | Static functions (alert, confirm, prompt) and ModalProvider detailed API |
169
167
  | `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.) |
168
+ | `knowledge/advanced-patterns.md` | Advanced patterns: AbortSignal, toast, nested modals, custom anchors, complex forms |
169
+ | `knowledge/type-definitions.md` | TypeScript interface definitions (ModalFrameProps, FooterComponentProps, PromptInputProps) |
@@ -1,6 +1,8 @@
1
1
  # Advanced Patterns
2
2
 
3
- ## Pattern 3: Modal Cancellation with AbortSignal
3
+ These patterns extend the basic Static API and `useModal` patterns documented in `SKILL.md`.
4
+
5
+ ## Pattern 1: Modal Cancellation with AbortSignal
4
6
 
5
7
  Programmatically cancel modals.
6
8
 
@@ -47,7 +49,7 @@ function ManualAbortControl() {
47
49
 
48
50
  ---
49
51
 
50
- ## Pattern 4: Toast Implementation
52
+ ## Pattern 2: Toast Implementation
51
53
 
52
54
  Implement auto-dismissing toast notifications.
53
55
 
@@ -104,7 +106,7 @@ toast('An error occurred.', 2000);
104
106
 
105
107
  ---
106
108
 
107
- ## Pattern 5: Nested Modals
109
+ ## Pattern 3: Nested Modals
108
110
 
109
111
  Display multiple modals sequentially in steps.
110
112
 
@@ -149,7 +151,7 @@ async function multiStepProcess() {
149
151
 
150
152
  ---
151
153
 
152
- ## Pattern 6: Custom Anchor
154
+ ## Pattern 4: Custom Anchor
153
155
 
154
156
  Render modals inside a specific DOM element.
155
157
 
@@ -191,7 +193,7 @@ function CustomAnchorExample() {
191
193
 
192
194
  ---
193
195
 
194
- ## Pattern 7: Complex Form Input
196
+ ## Pattern 5: Complex Form Input
195
197
 
196
198
  Collect complex form data using prompt.
197
199
 
@@ -258,7 +260,7 @@ async function collectUserInfo() {
258
260
 
259
261
  ---
260
262
 
261
- ## Pattern 8: Conditional Modal Styles
263
+ ## Pattern 6: Conditional Modal Styles
262
264
 
263
265
  Display different styled modals based on situation.
264
266
 
@@ -20,13 +20,7 @@ function MyComponent() {
20
20
  }
21
21
  ```
22
22
 
23
- **Key Feature**: Modals are automatically cleaned up when component unmounts.
24
-
25
- | Feature | Static Handlers | useModal Hook |
26
- |------|------------|-------------|
27
- | Lifecycle | Independent | Tied to component |
28
- | Cleanup | Manual | Auto on unmount |
29
- | Usage Location | Anywhere | Inside React components |
23
+ **Key Feature**: Modals are automatically cleaned up when component unmounts. For a comparison with the Static API, see `SKILL.md`.
30
24
 
31
25
  ---
32
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lerx/promise-modal",
3
- "version": "0.11.1",
3
+ "version": "0.12.1",
4
4
  "description": "Universal React modal utility that can be used outside React components with promise-based results for alert, confirm, and prompt modals",
5
5
  "keywords": [
6
6
  "react",
@@ -48,13 +48,15 @@
48
48
  "README.md"
49
49
  ],
50
50
  "scripts": {
51
- "build": "rollup -c && yarn build:types",
51
+ "build": "rollup -c && yarn build:types && yarn build:hashes",
52
+ "build:hashes": "claude-build-hashes",
52
53
  "build:publish:npm": "yarn build && yarn publish:npm",
53
54
  "build:types": "node ../../aileron/script/build/buildTypes.mjs",
54
55
  "build-storybook": "storybook build",
55
56
  "format": "prettier --write \"src/**/*.{ts,tsx}\"",
56
57
  "lint": "eslint \"src/**/*.{ts,tsx}\"",
57
58
  "make-dependency-graph": "npx depcruise src --config .dependency-cruiser.js --output-type dot > dependency-graph.dot && dot -Tpng dependency-graph.dot -o dependency-graph.png",
59
+ "prepublishOnly": "yarn build",
58
60
  "publish:npm": "yarn npm publish --access public",
59
61
  "size-limit": "size-limit",
60
62
  "start": "yarn build && yarn storybook",
@@ -65,9 +67,10 @@
65
67
  "version:patch": "yarn version patch"
66
68
  },
67
69
  "dependencies": {
68
- "@winglet/common-utils": "^0.11.1",
69
- "@winglet/react-utils": "^0.11.2",
70
- "@winglet/style-utils": "^0.11.1"
70
+ "@slats/claude-assets-sync": "^0.3.0",
71
+ "@winglet/common-utils": "^0.12.1",
72
+ "@winglet/react-utils": "^0.12.1",
73
+ "@winglet/style-utils": "^0.12.1"
71
74
  },
72
75
  "devDependencies": {
73
76
  "@types/react": "^19.0.0",
@@ -1,760 +0,0 @@
1
- # Promise Modal Guide Command
2
-
3
- **Package**: `@lerx/promise-modal`
4
- **Expert Skill**: `promise-modal-expert` (directory-based skill)
5
-
6
- ## Purpose
7
-
8
- This command provides an interactive Q&A guide for users of the `@lerx/promise-modal` library. When invoked, help users understand and effectively use the library based on their specific questions and use cases.
9
-
10
- ## Activation
11
-
12
- This command should be used when users:
13
- - Have questions about `@lerx/promise-modal`
14
- - Need help implementing modals
15
- - Want to understand specific API usage
16
- - Experience library-related issues
17
- - Need code examples for specific scenarios
18
-
19
- ## Response Strategy
20
-
21
- ### Step 1: Identify Question Category
22
-
23
- Classify user questions into one of the following:
24
-
25
- 1. **Getting Started** - Installation, setup, basic usage
26
- 2. **API Usage** - Specific function/hook usage
27
- 3. **Customization** - Custom components, styling, theming
28
- 4. **Advanced Patterns** - Toast, nested modals, animations, AbortSignal
29
- 5. **Troubleshooting** - Error resolution, debugging
30
-
31
- ### Step 2: Provide Structured Response
32
-
33
- Follow this response pattern for each category:
34
-
35
- ---
36
-
37
- ## Category: Getting Started
38
-
39
- ### Related Questions:
40
- - "How do I install promise-modal?"
41
- - "How do I set up the Provider?"
42
- - "What is the basic usage?"
43
-
44
- ### Response Template:
45
-
46
- ```markdown
47
- ## Installation
48
-
49
- ```bash
50
- yarn add @lerx/promise-modal
51
- # or
52
- npm install @lerx/promise-modal
53
- ```
54
-
55
- ## Basic Setup
56
-
57
- 1. Wrap your app with ModalProvider:
58
-
59
- ```tsx
60
- import { ModalProvider } from '@lerx/promise-modal';
61
- import { useLocation } from 'react-router-dom'; // Optional router integration
62
-
63
- function App() {
64
- return (
65
- <ModalProvider
66
- usePathname={useLocation} // Router integration (auto-close on path change)
67
- options={{
68
- duration: '200ms',
69
- backdrop: 'rgba(0, 0, 0, 0.35)',
70
- manualDestroy: true,
71
- }}
72
- >
73
- <YourApp />
74
- </ModalProvider>
75
- );
76
- }
77
- ```
78
-
79
- 2. Use modal functions anywhere:
80
-
81
- ```tsx
82
- import { alert, confirm, prompt } from '@lerx/promise-modal';
83
-
84
- // Simple alert
85
- await alert({ title: 'Alert', content: 'Hello!' });
86
-
87
- // Confirmation
88
- const result = await confirm({ title: 'Confirm', content: 'Continue?' });
89
-
90
- // User input
91
- const value = await prompt<string>({
92
- title: 'Input',
93
- defaultValue: '',
94
- Input: ({ value, onChange }) => (
95
- <input value={value} onChange={(e) => onChange(e.target.value)} />
96
- ),
97
- });
98
- ```
99
- ```
100
-
101
- ---
102
-
103
- ## Category: API Usage
104
-
105
- ### `alert` Questions:
106
-
107
- ```markdown
108
- ## alert() API
109
-
110
- Opens a simple notification modal.
111
-
112
- **Signature:**
113
- ```typescript
114
- alert(options: AlertProps): Promise<void>
115
- ```
116
-
117
- **Key Options:**
118
- - `title`: Modal title (ReactNode)
119
- - `content`: Modal content (ReactNode or Component)
120
- - `subtype`: 'info' | 'success' | 'warning' | 'error'
121
- - `dimmed`: Darken background (boolean)
122
- - `closeOnBackdropClick`: Close on backdrop click (boolean)
123
- - `signal`: AbortSignal for modal cancellation
124
-
125
- **Example:**
126
- ```tsx
127
- await alert({
128
- title: 'Success',
129
- content: 'Task completed!',
130
- subtype: 'success',
131
- dimmed: true,
132
- });
133
- ```
134
- ```
135
-
136
- ### `confirm` Questions:
137
-
138
- ```markdown
139
- ## confirm() API
140
-
141
- Opens a confirmation modal with yes/no options.
142
-
143
- **Signature:**
144
- ```typescript
145
- confirm(options: ConfirmProps): Promise<boolean>
146
- ```
147
-
148
- **Return Value:**
149
- - `true` - User clicked confirm
150
- - `false` - User clicked cancel or backdrop
151
-
152
- **Example:**
153
- ```tsx
154
- const shouldDelete = await confirm({
155
- title: 'Delete Item',
156
- content: 'Are you sure you want to delete this?',
157
- footer: {
158
- confirm: 'Delete',
159
- cancel: 'Cancel',
160
- },
161
- });
162
-
163
- if (shouldDelete) {
164
- // Perform deletion
165
- }
166
- ```
167
- ```
168
-
169
- ### `prompt` Questions:
170
-
171
- ```markdown
172
- ## prompt<T>() API
173
-
174
- Opens a modal to collect user input.
175
-
176
- **Signature:**
177
- ```typescript
178
- prompt<T>(options: PromptProps<T>): Promise<T>
179
- ```
180
-
181
- **Key Options:**
182
- - `Input`: Custom input component (required)
183
- - `defaultValue`: Initial value
184
- - `disabled`: Function to disable confirm button
185
-
186
- **Simple Example:**
187
- ```tsx
188
- const name = await prompt<string>({
189
- title: 'Enter Name',
190
- defaultValue: '',
191
- Input: ({ value, onChange }) => (
192
- <input
193
- value={value}
194
- onChange={(e) => onChange(e.target.value)}
195
- />
196
- ),
197
- disabled: (value) => value.trim() === '',
198
- });
199
- ```
200
-
201
- **Complex Data Example:**
202
- ```tsx
203
- interface UserData {
204
- name: string;
205
- email: string;
206
- }
207
-
208
- const userData = await prompt<UserData>({
209
- title: 'User Information',
210
- defaultValue: { name: '', email: '' },
211
- Input: ({ value, onChange }) => (
212
- <form>
213
- <input
214
- value={value.name}
215
- onChange={(e) => onChange({ ...value, name: e.target.value })}
216
- placeholder="Name"
217
- />
218
- <input
219
- value={value.email}
220
- onChange={(e) => onChange({ ...value, email: e.target.value })}
221
- placeholder="Email"
222
- />
223
- </form>
224
- ),
225
- });
226
- ```
227
- ```
228
-
229
- ### `useModal` Questions:
230
-
231
- ```markdown
232
- ## useModal() Hook
233
-
234
- Returns modal handlers tied to component lifecycle.
235
-
236
- **Key Feature:** Automatic cleanup on component unmount.
237
-
238
- **Example:**
239
- ```tsx
240
- import { useModal } from '@lerx/promise-modal';
241
-
242
- function MyComponent() {
243
- const modal = useModal();
244
-
245
- const handleAction = async () => {
246
- // These modals will auto-close if component unmounts
247
- if (await modal.confirm({ content: 'Proceed?' })) {
248
- await modal.alert({ content: 'Done!' });
249
- }
250
- };
251
-
252
- return <button onClick={handleAction}>Execute</button>;
253
- }
254
- ```
255
-
256
- **With Configuration:**
257
- ```tsx
258
- const modal = useModal({
259
- ForegroundComponent: CustomForeground,
260
- dimmed: true,
261
- duration: 300,
262
- });
263
- ```
264
- ```
265
-
266
- ---
267
-
268
- ## Category: Configuration Priority
269
-
270
- ```markdown
271
- ## Configuration Priority
272
-
273
- Configuration is applied hierarchically:
274
-
275
- ```
276
- Provider Config (Lowest) < Hook Config < Handler Config (Highest)
277
- ```
278
-
279
- **Example:**
280
- ```tsx
281
- // Provider level: Global defaults
282
- <ModalProvider options={{ duration: '500ms', closeOnBackdropClick: true }}>
283
- <App />
284
- </ModalProvider>
285
-
286
- // Hook level: Component defaults (overrides Provider config)
287
- const modal = useModal({
288
- ForegroundComponent: CustomForeground,
289
- });
290
-
291
- // Handler level: Individual modal (overrides Hook config)
292
- modal.alert({
293
- title: 'Alert',
294
- duration: 200, // Override 500ms → 200ms
295
- ForegroundComponent: SpecialForeground, // Override CustomForeground
296
- });
297
- ```
298
- ```
299
-
300
- ---
301
-
302
- ## Category: Customization
303
-
304
- ### Component Customization Questions:
305
-
306
- ```markdown
307
- ## Custom Components
308
-
309
- ### Custom Foreground
310
-
311
- ```tsx
312
- const CustomForeground = ({ children, visible, id }) => {
313
- const ref = useRef(null);
314
- const { duration } = useModalDuration();
315
-
316
- useModalAnimation(visible, {
317
- onVisible: () => ref.current?.classList.add('active'),
318
- onHidden: () => ref.current?.classList.remove('active'),
319
- });
320
-
321
- useDestroyAfter(id, duration);
322
-
323
- return (
324
- <div
325
- ref={ref}
326
- style={{
327
- background: 'white',
328
- borderRadius: 12,
329
- padding: 24,
330
- maxWidth: 500,
331
- }}
332
- >
333
- {children}
334
- </div>
335
- );
336
- };
337
-
338
- // Use in Provider
339
- <ModalProvider ForegroundComponent={CustomForeground}>
340
- <App />
341
- </ModalProvider>
342
-
343
- // Or in individual modal
344
- alert({
345
- content: 'Hello',
346
- ForegroundComponent: CustomForeground,
347
- });
348
- ```
349
-
350
- ### Custom Footer
351
-
352
- ```tsx
353
- const CustomFooter = ({ onConfirm, onClose, type, disabled }) => (
354
- <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
355
- {type !== 'alert' && (
356
- <button onClick={onClose}>Cancel</button>
357
- )}
358
- <button onClick={() => onConfirm()} disabled={disabled}>
359
- Confirm
360
- </button>
361
- </div>
362
- );
363
- ```
364
- ```
365
-
366
- ### Styling/Theming Questions:
367
-
368
- ```markdown
369
- ## Theming with Context
370
-
371
- ```tsx
372
- <ModalProvider
373
- context={{
374
- theme: 'dark',
375
- primaryColor: '#007bff',
376
- }}
377
- >
378
- <App />
379
- </ModalProvider>
380
-
381
- // Access in custom components
382
- const CustomTitle = ({ children, context }) => (
383
- <h2 style={{ color: context.theme === 'dark' ? '#fff' : '#333' }}>
384
- {children}
385
- </h2>
386
- );
387
- ```
388
- ```
389
-
390
- ---
391
-
392
- ## Category: Advanced Patterns
393
-
394
- ### AbortSignal Questions:
395
-
396
- ```markdown
397
- ## Modal Cancellation with AbortSignal
398
-
399
- Programmatically cancel modals.
400
-
401
- **Basic Usage:**
402
- ```tsx
403
- const controller = new AbortController();
404
-
405
- alert({
406
- title: 'Cancelable Modal',
407
- content: 'Auto-closes in 3 seconds.',
408
- signal: controller.signal,
409
- });
410
-
411
- // Cancel modal after 3 seconds
412
- setTimeout(() => {
413
- controller.abort();
414
- }, 3000);
415
- ```
416
-
417
- **Manual Abort Control:**
418
- ```tsx
419
- function ManualAbortControl() {
420
- const [controller, setController] = useState<AbortController | null>(null);
421
-
422
- const handleOpen = () => {
423
- const newController = new AbortController();
424
- setController(newController);
425
-
426
- alert({
427
- title: 'Manual Cancel',
428
- content: 'Click "Cancel" button to close modal.',
429
- signal: newController.signal,
430
- closeOnBackdropClick: false,
431
- }).then(() => {
432
- setController(null);
433
- });
434
- };
435
-
436
- const handleAbort = () => {
437
- if (controller) {
438
- controller.abort();
439
- }
440
- };
441
-
442
- return (
443
- <div>
444
- <button onClick={handleOpen} disabled={!!controller}>Open Modal</button>
445
- <button onClick={handleAbort} disabled={!controller}>Cancel Modal</button>
446
- </div>
447
- );
448
- }
449
- ```
450
-
451
- **Batch Cancel Multiple Modals:**
452
- ```tsx
453
- const controllers: AbortController[] = [];
454
-
455
- for (let i = 0; i < 3; i++) {
456
- const controller = new AbortController();
457
- controllers.push(controller);
458
-
459
- alert({
460
- title: `Modal ${i + 1}`,
461
- signal: controller.signal,
462
- });
463
- }
464
-
465
- // Cancel all modals
466
- controllers.forEach((c) => c.abort());
467
- ```
468
- ```
469
-
470
- ### Toast Implementation Questions:
471
-
472
- ```markdown
473
- ## Toast Implementation
474
-
475
- ```tsx
476
- import { alert, useModalAnimation, useDestroyAfter, useModalDuration } from '@lerx/promise-modal';
477
-
478
- const ToastForeground = ({ id, visible, children, onClose, onDestroy }) => {
479
- const ref = useRef(null);
480
- const { duration } = useModalDuration();
481
-
482
- useEffect(() => {
483
- const timer = setTimeout(onClose, 3000);
484
- return () => clearTimeout(timer);
485
- }, [onClose]);
486
-
487
- useModalAnimation(visible, {
488
- onVisible: () => ref.current?.classList.add('visible'),
489
- onHidden: () => ref.current?.classList.remove('visible'),
490
- });
491
-
492
- useDestroyAfter(id, duration);
493
-
494
- return (
495
- <div
496
- ref={ref}
497
- style={{
498
- position: 'fixed',
499
- bottom: 20,
500
- left: '50%',
501
- transform: 'translateX(-50%)',
502
- background: '#333',
503
- color: 'white',
504
- padding: '12px 24px',
505
- borderRadius: 8,
506
- opacity: 0,
507
- transition: 'opacity 300ms',
508
- }}
509
- className={visible ? 'visible' : ''}
510
- >
511
- {children}
512
- </div>
513
- );
514
- };
515
-
516
- // Pattern for removing previous toast
517
- let onDestroyPrevToast: () => void;
518
-
519
- export const toast = (message: ReactNode, duration = 1250) => {
520
- onDestroyPrevToast?.(); // Remove previous toast
521
-
522
- return alert({
523
- content: message,
524
- ForegroundComponent: (props) => {
525
- onDestroyPrevToast = props.onDestroy;
526
- return <ToastForeground {...props} />;
527
- },
528
- footer: false,
529
- dimmed: false,
530
- closeOnBackdropClick: false,
531
- });
532
- };
533
-
534
- // Usage
535
- toast('Task completed successfully!');
536
- ```
537
- ```
538
-
539
- ### Nested Modal Questions:
540
-
541
- ```markdown
542
- ## Nested Modals
543
-
544
- ```tsx
545
- async function multiStepWizard() {
546
- // Step 1: Confirm
547
- const proceed = await confirm({
548
- title: 'Start Wizard',
549
- content: 'We will guide you through the setup.',
550
- });
551
-
552
- if (!proceed) return;
553
-
554
- // Step 2: User input
555
- const username = await prompt<string>({
556
- title: 'Step 1: Username',
557
- defaultValue: '',
558
- Input: ({ value, onChange }) => (
559
- <input value={value} onChange={(e) => onChange(e.target.value)} />
560
- ),
561
- });
562
-
563
- if (!username) return;
564
-
565
- // Step 3: Confirm and complete
566
- const confirmed = await confirm({
567
- title: 'Step 2: Confirm',
568
- content: `Create account "${username}"?`,
569
- });
570
-
571
- if (confirmed) {
572
- await alert({
573
- title: 'Complete!',
574
- content: `Welcome, ${username}!`,
575
- subtype: 'success',
576
- });
577
- }
578
- }
579
- ```
580
- ```
581
-
582
- ### Custom Anchor Questions:
583
-
584
- ```markdown
585
- ## Custom Modal Anchor
586
-
587
- ```tsx
588
- import { ModalProvider, useInitializeModal, alert } from '@lerx/promise-modal';
589
-
590
- function CustomAnchorExample() {
591
- const { initialize } = useInitializeModal({ mode: 'manual' });
592
- const containerRef = useRef<HTMLDivElement>(null);
593
-
594
- useEffect(() => {
595
- if (containerRef.current) {
596
- initialize(containerRef.current);
597
- }
598
- }, [initialize]);
599
-
600
- return (
601
- <div>
602
- {/* Modals render inside this container */}
603
- <div
604
- ref={containerRef}
605
- style={{
606
- position: 'relative',
607
- width: '100%',
608
- height: 500,
609
- overflow: 'hidden',
610
- }}
611
- />
612
- <button onClick={() => alert({ content: 'Inside custom container!' })}>
613
- Show Modal
614
- </button>
615
- </div>
616
- );
617
- }
618
- ```
619
- ```
620
-
621
- ---
622
-
623
- ## Category: Troubleshooting
624
-
625
- ### Common Issues:
626
-
627
- ```markdown
628
- ## Troubleshooting Guide
629
-
630
- ### Modal Not Appearing
631
-
632
- **Cause 1:** Missing ModalProvider
633
- ```tsx
634
- // ❌ Wrong
635
- function App() {
636
- return <MyApp />;
637
- }
638
-
639
- // ✅ Correct
640
- function App() {
641
- return (
642
- <ModalProvider>
643
- <MyApp />
644
- </ModalProvider>
645
- );
646
- }
647
- ```
648
-
649
- **Cause 2:** z-index conflict
650
- ```css
651
- /* Ensure modal has highest z-index */
652
- .modal-container {
653
- z-index: 9999;
654
- }
655
- ```
656
-
657
- ### Modal Not Closing
658
-
659
- **Cause 1:** manualDestroy is true
660
- ```tsx
661
- // Modal won't auto-close
662
- alert({ content: 'Hello', manualDestroy: true });
663
-
664
- // Must call onDestroy manually
665
- ```
666
-
667
- **Cause 2:** closeOnBackdropClick is false
668
- ```tsx
669
- // Won't close on backdrop click
670
- alert({ content: 'Hello', closeOnBackdropClick: false });
671
- ```
672
-
673
- ### prompt TypeScript Errors
674
-
675
- **Cause:** Missing generic type
676
- ```tsx
677
- // ❌ Wrong - TypeScript can't infer type
678
- const value = await prompt({ ... });
679
-
680
- // ✅ Correct - Specify type
681
- const value = await prompt<string>({ ... });
682
- ```
683
-
684
- ### Animation Not Working
685
-
686
- **Cause:** Not using useModalAnimation
687
- ```tsx
688
- // ❌ Wrong - Checking visible directly
689
- if (visible) element.classList.add('show');
690
-
691
- // ✅ Correct - Use hook
692
- useModalAnimation(visible, {
693
- onVisible: () => element.classList.add('show'),
694
- onHidden: () => element.classList.remove('show'),
695
- });
696
- ```
697
- ```
698
-
699
- ---
700
-
701
- ## Response Format
702
-
703
- When answering questions, follow this structure:
704
-
705
- 1. **Brief Answer**: One-line summary
706
- 2. **Code Example**: Working code snippet
707
- 3. **Explanation**: Key points to understand
708
- 4. **Related API**: Links to related features
709
- 5. **Common Pitfalls**: What to avoid
710
-
711
- ---
712
-
713
- ## Example Interaction
714
-
715
- **User**: "How do I disable the confirm button until a required field is filled?"
716
-
717
- **Response**:
718
- ```markdown
719
- Use the `disabled` option in `prompt()`:
720
-
721
- ```tsx
722
- const email = await prompt<string>({
723
- title: 'Enter Email',
724
- defaultValue: '',
725
- Input: ({ value, onChange }) => (
726
- <input
727
- type="email"
728
- value={value}
729
- onChange={(e) => onChange(e.target.value)}
730
- placeholder="your@email.com"
731
- />
732
- ),
733
- // Disable confirm button until valid email
734
- disabled: (value) => !value.includes('@') || value.length < 5,
735
- });
736
- ```
737
-
738
- **Key Points:**
739
- - `disabled` receives the current value
740
- - Returns `true` to disable confirm button
741
- - Updates reactively as user types
742
-
743
- **Related:** `PromptInputProps`, `FooterComponentProps.disabled`
744
- ```
745
-
746
- ---
747
-
748
- ## Knowledge Sources
749
-
750
- For more detailed information, refer to the related skill's in-depth knowledge:
751
-
752
- | Topic | Knowledge File |
753
- |------|-----------|
754
- | Comprehensive Guide | `promise-modal-expert` (directory skill) |
755
- | API Reference | `knowledge/api-reference.md` |
756
- | Hooks Reference | `knowledge/hooks-reference.md` |
757
- | Advanced Patterns | `knowledge/advanced-patterns.md` |
758
- | Type Definitions | `knowledge/type-definitions.md` |
759
-
760
- See SPECIFICATION documentation for full API specs.