@lerx/promise-modal 0.8.3 → 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 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], void>);
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): void;
20
+ static open(modal: Modal): ModalNode;
18
21
  }
@@ -1 +1,2 @@
1
1
  export declare const anchor: string;
2
+ export declare const backdrop: string;
@@ -1,159 +1,27 @@
1
- import type { ComponentType, ReactNode } from 'react';
2
- import type { AlertContentProps, AlertFooterRender, BackgroundComponent, FooterOptions, ForegroundComponent, ModalBackground } from '../../types';
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
- * 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.
4
+ * Creates and manages an Alert modal.
22
5
  *
23
6
  * @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
- * ```
7
+ * @param args - Alert modal configuration options
8
+ * @returns Object containing modalNode and promiseHandler
97
9
  *
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
- * ```
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
- * 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,
16
+ * const { modalNode, promiseHandler } = alertHandler({
17
+ * title: 'Success',
18
+ * content: 'Operation completed successfully!',
127
19
  * });
128
- * ```
129
20
  *
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
- * }
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 alert: <BackgroundValue = any>({ group, subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }: AlertProps<BackgroundValue>) => Promise<void>;
159
- export {};
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 { ComponentType, ReactNode } from 'react';
2
- import type { BackgroundComponent, ConfirmContentProps, ConfirmFooterRender, FooterOptions, ForegroundComponent, ModalBackground } from '../../types';
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
- * 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.
4
+ * Creates and manages a Confirm modal.
22
5
  *
23
6
  * @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
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 shouldDelete = await confirm({
18
+ * const { modalNode, promiseHandler } = confirmHandler({
31
19
  * title: 'Delete Item?',
32
20
  * content: 'This action cannot be undone.',
33
21
  * });
34
22
  *
35
- * if (shouldDelete) {
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 confirm: <BackgroundValue = any>({ group, subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }: ConfirmProps<BackgroundValue>) => Promise<boolean>;
165
- export {};
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 { alert } from './alert';
2
- export { confirm } from './confirm';
3
- export { prompt } from './prompt';
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';