@lerx/promise-modal 0.8.4 → 0.8.5
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 +53 -0
- package/dist/app/ModalManager.d.ts +5 -2
- package/dist/core/handle/alert.d.ts +16 -148
- package/dist/core/handle/confirm.d.ts +18 -151
- package/dist/core/handle/index.d.ts +5 -3
- package/dist/core/handle/prompt.d.ts +23 -212
- package/dist/core/handle/static.d.ts +105 -0
- package/dist/core/handle/type.d.ts +99 -0
- package/dist/core/node/ModalNode/AbstractNode.d.ts +7 -5
- package/dist/core/node/ModalNode/AlertNode.d.ts +2 -2
- package/dist/core/node/ModalNode/ConfirmNode.d.ts +2 -2
- package/dist/core/node/ModalNode/PromptNode.d.ts +2 -2
- package/dist/helpers/closeModal.d.ts +2 -0
- package/dist/helpers/subscribeAbortSignal.d.ts +2 -0
- package/dist/hooks/useModal.d.ts +6 -0
- package/dist/index.cjs +198 -153
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +199 -155
- package/dist/providers/ModalManagerContext/ModalManagerContext.d.ts +0 -1
- package/dist/providers/ModalManagerContext/index.d.ts +1 -1
- package/dist/providers/ModalManagerContext/useModalManagerContext.d.ts +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/types/base.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -868,6 +868,59 @@ function App() {
|
|
|
868
868
|
|
|
869
869
|
### Hooks
|
|
870
870
|
|
|
871
|
+
#### `useModal`
|
|
872
|
+
|
|
873
|
+
Returns modal handler functions that share the component's lifecycle.
|
|
874
|
+
|
|
875
|
+
**Key Features:**
|
|
876
|
+
|
|
877
|
+
Unlike static handlers (`alert`, `confirm`, `prompt`), modals created with `useModal` automatically clean up when the component unmounts. This is useful when you want to ensure that modals opened within a specific component are automatically closed when that component is removed.
|
|
878
|
+
|
|
879
|
+
**Returns:**
|
|
880
|
+
|
|
881
|
+
- `alert`: Alert modal handler function
|
|
882
|
+
- `confirm`: Confirm modal handler function
|
|
883
|
+
- `prompt`: Prompt modal handler function
|
|
884
|
+
|
|
885
|
+
```typescript
|
|
886
|
+
import { useModal } from '@lerx/promise-modal';
|
|
887
|
+
|
|
888
|
+
function MyComponent() {
|
|
889
|
+
const modal = useModal();
|
|
890
|
+
|
|
891
|
+
const handleShowAlert = async () => {
|
|
892
|
+
// Modal will be automatically cleaned up when MyComponent unmounts
|
|
893
|
+
await modal.alert({
|
|
894
|
+
title: 'Notice',
|
|
895
|
+
content: 'This modal is tied to the component lifecycle.',
|
|
896
|
+
});
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
const handleShowConfirm = async () => {
|
|
900
|
+
const result = await modal.confirm({
|
|
901
|
+
title: 'Confirm',
|
|
902
|
+
content: 'Do you want to proceed?',
|
|
903
|
+
});
|
|
904
|
+
console.log('Result:', result);
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
return (
|
|
908
|
+
<div>
|
|
909
|
+
<button onClick={handleShowAlert}>Show Alert</button>
|
|
910
|
+
<button onClick={handleShowConfirm}>Show Confirm</button>
|
|
911
|
+
</div>
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
**Comparison with Static Handlers:**
|
|
917
|
+
|
|
918
|
+
| Feature | Static Handlers | useModal Hook |
|
|
919
|
+
| -------------- | ----------------------------- | ---------------------------- |
|
|
920
|
+
| Lifecycle | Independent of components | Tied to component lifecycle |
|
|
921
|
+
| Cleanup | Manual cleanup required | Automatic cleanup on unmount |
|
|
922
|
+
| Usage Location | Anywhere (even outside React) | Inside React components only |
|
|
923
|
+
|
|
871
924
|
#### `useModalOptions`
|
|
872
925
|
|
|
873
926
|
Read global options for modals.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Fn } from '../@aileron/declare';
|
|
2
|
+
import type { ModalNode } from '../core';
|
|
2
3
|
import type { Modal } from '../types';
|
|
3
4
|
export declare class ModalManager {
|
|
4
5
|
#private;
|
|
@@ -9,10 +10,12 @@ export declare class ModalManager {
|
|
|
9
10
|
}): HTMLElement;
|
|
10
11
|
static get anchored(): boolean;
|
|
11
12
|
static get prerender(): Modal[];
|
|
12
|
-
static set openHandler(handler: Fn<[Modal],
|
|
13
|
+
static set openHandler(handler: Fn<[Modal], ModalNode>);
|
|
14
|
+
static set refreshHandler(handler: Fn<[], void>);
|
|
15
|
+
static refresh(): void;
|
|
13
16
|
static defineStyleSheet(styleId: string, css: string): void;
|
|
14
17
|
static applyStyleSheet(): void;
|
|
15
18
|
static getHashedClassNames(styleId: string): string;
|
|
16
19
|
static reset(): void;
|
|
17
|
-
static open(modal: Modal):
|
|
20
|
+
static open(modal: Modal): ModalNode;
|
|
18
21
|
}
|
|
@@ -1,159 +1,27 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
interface AlertProps<BackgroundValue> {
|
|
4
|
-
group?: string;
|
|
5
|
-
subtype?: 'info' | 'success' | 'warning' | 'error';
|
|
6
|
-
title?: ReactNode;
|
|
7
|
-
subtitle?: ReactNode;
|
|
8
|
-
content?: ReactNode | ComponentType<AlertContentProps>;
|
|
9
|
-
background?: ModalBackground<BackgroundValue>;
|
|
10
|
-
footer?: AlertFooterRender | Pick<FooterOptions, 'confirm' | 'hideConfirm'> | false;
|
|
11
|
-
dimmed?: boolean;
|
|
12
|
-
manualDestroy?: boolean;
|
|
13
|
-
closeOnBackdropClick?: boolean;
|
|
14
|
-
ForegroundComponent?: ForegroundComponent;
|
|
15
|
-
BackgroundComponent?: BackgroundComponent;
|
|
16
|
-
}
|
|
1
|
+
import type { AlertNode } from '../../core';
|
|
2
|
+
import type { AlertProps } from './type';
|
|
17
3
|
/**
|
|
18
|
-
*
|
|
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.
|
|
4
|
+
* Creates and manages an Alert modal.
|
|
22
5
|
*
|
|
23
6
|
* @typeParam BackgroundValue - Type of background data passed to BackgroundComponent
|
|
24
|
-
* @param
|
|
25
|
-
* @returns
|
|
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
|
-
* ```
|
|
7
|
+
* @param args - Alert modal configuration options
|
|
8
|
+
* @returns Object containing modalNode and promiseHandler
|
|
97
9
|
*
|
|
98
|
-
* @
|
|
99
|
-
*
|
|
100
|
-
*
|
|
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
|
-
* ```
|
|
10
|
+
* @remarks
|
|
11
|
+
* - modalNode: The created modal node instance
|
|
12
|
+
* - promiseHandler: Promise that resolves when the modal is closed
|
|
115
13
|
*
|
|
116
14
|
* @example
|
|
117
|
-
* Non-closeable critical alert:
|
|
118
15
|
* ```tsx
|
|
119
|
-
*
|
|
120
|
-
* title: '
|
|
121
|
-
* content: '
|
|
122
|
-
* closeOnBackdropClick: false,
|
|
123
|
-
* footer: {
|
|
124
|
-
* confirm: 'Got it',
|
|
125
|
-
* },
|
|
126
|
-
* manualDestroy: true,
|
|
16
|
+
* const { modalNode, promiseHandler } = alertHandler({
|
|
17
|
+
* title: 'Success',
|
|
18
|
+
* content: 'Operation completed successfully!',
|
|
127
19
|
* });
|
|
128
|
-
* ```
|
|
129
20
|
*
|
|
130
|
-
*
|
|
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
|
-
* }
|
|
21
|
+
* await promiseHandler; // Wait until modal is closed
|
|
150
22
|
* ```
|
|
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
23
|
*/
|
|
158
|
-
export declare const
|
|
159
|
-
|
|
24
|
+
export declare const alertHandler: <BackgroundValue = any>(args: AlertProps<BackgroundValue>) => {
|
|
25
|
+
readonly modalNode: AlertNode<BackgroundValue>;
|
|
26
|
+
readonly promiseHandler: Promise<void>;
|
|
27
|
+
};
|
|
@@ -1,165 +1,32 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
interface ConfirmProps<BackgroundValue> {
|
|
4
|
-
group?: string;
|
|
5
|
-
subtype?: 'info' | 'success' | 'warning' | 'error';
|
|
6
|
-
title?: ReactNode;
|
|
7
|
-
subtitle?: ReactNode;
|
|
8
|
-
content?: ReactNode | ComponentType<ConfirmContentProps>;
|
|
9
|
-
background?: ModalBackground<BackgroundValue>;
|
|
10
|
-
footer?: ConfirmFooterRender | FooterOptions | false;
|
|
11
|
-
dimmed?: boolean;
|
|
12
|
-
manualDestroy?: boolean;
|
|
13
|
-
closeOnBackdropClick?: boolean;
|
|
14
|
-
ForegroundComponent?: ForegroundComponent;
|
|
15
|
-
BackgroundComponent?: BackgroundComponent;
|
|
16
|
-
}
|
|
1
|
+
import type { ConfirmNode } from '../../core';
|
|
2
|
+
import type { ConfirmProps } from './type';
|
|
17
3
|
/**
|
|
18
|
-
*
|
|
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.
|
|
4
|
+
* Creates and manages a Confirm modal.
|
|
22
5
|
*
|
|
23
6
|
* @typeParam BackgroundValue - Type of background data passed to BackgroundComponent
|
|
24
|
-
* @param
|
|
25
|
-
* @returns
|
|
7
|
+
* @param args - Confirm modal configuration options
|
|
8
|
+
* @returns Object containing modalNode and promiseHandler
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* - modalNode: The created modal node instance
|
|
12
|
+
* - promiseHandler: Promise that resolves to true if confirmed, false if cancelled
|
|
13
|
+
* - Returns true only when explicitly confirmed via the confirm action
|
|
14
|
+
* - Returns false for cancel action or backdrop click (if enabled)
|
|
26
15
|
*
|
|
27
16
|
* @example
|
|
28
|
-
* Basic confirmation:
|
|
29
17
|
* ```tsx
|
|
30
|
-
* const
|
|
18
|
+
* const { modalNode, promiseHandler } = confirmHandler({
|
|
31
19
|
* title: 'Delete Item?',
|
|
32
20
|
* content: 'This action cannot be undone.',
|
|
33
21
|
* });
|
|
34
22
|
*
|
|
35
|
-
*
|
|
23
|
+
* const confirmed = await promiseHandler;
|
|
24
|
+
* if (confirmed) {
|
|
36
25
|
* await deleteItem();
|
|
37
26
|
* }
|
|
38
27
|
* ```
|
|
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
28
|
*/
|
|
164
|
-
export declare const
|
|
165
|
-
|
|
29
|
+
export declare const confirmHandler: <BackgroundValue = any>(args: ConfirmProps<BackgroundValue>) => {
|
|
30
|
+
readonly modalNode: ConfirmNode<BackgroundValue>;
|
|
31
|
+
readonly promiseHandler: Promise<boolean>;
|
|
32
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
1
|
+
export { alertHandler } from './alert';
|
|
2
|
+
export { confirmHandler } from './confirm';
|
|
3
|
+
export { promptHandler } from './prompt';
|
|
4
|
+
export { alert, confirm, prompt } from './static';
|
|
5
|
+
export type { AlertProps, ConfirmProps, PromptProps } from './type';
|