@lerx/promise-modal 0.12.2 → 0.12.3

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/index.mjs CHANGED
@@ -2,13 +2,13 @@ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { createContext, useContext, useMemo, forwardRef, memo, useRef, useState, useLayoutEffect, useCallback, Fragment, useEffect, useImperativeHandle } from 'react';
3
3
  import { convertMsFromDuration } from '@winglet/common-utils/convert';
4
4
  import { useMemorize, useSnapshot, useReference, useOnMountLayout, useHandle, useVersion, useOnMount, useOnUnmount } from '@winglet/react-utils/hook';
5
+ import { map } from '@winglet/common-utils/array';
5
6
  import { polynomialHash } from '@winglet/common-utils/hash';
6
7
  import { getRandomString, counterFactory } from '@winglet/common-utils/lib';
7
8
  import { styleManagerFactory, destroyScope } from '@winglet/style-utils/style-manager';
8
9
  import { compressCss, cxLite } from '@winglet/style-utils/util';
9
10
  import { isPlainObject, isString, isFunction } from '@winglet/common-utils/filter';
10
11
  import { createPortal } from 'react-dom';
11
- import { map } from '@winglet/common-utils/array';
12
12
  import { withErrorBoundary } from '@winglet/react-utils/hoc';
13
13
  import { renderComponent } from '@winglet/react-utils/render';
14
14
 
@@ -28,11 +28,38 @@ class ModalManager {
28
28
  return ModalManager.__anchor__ !== null;
29
29
  }
30
30
  static get prerender() {
31
- return ModalManager.__prerenderList__;
31
+ return map(ModalManager.__prerenderList__, (entry) => entry.modal);
32
32
  }
33
33
  static set openHandler(handler) {
34
34
  ModalManager.__openHandler__ = handler;
35
+ const entries = ModalManager.__prerenderList__;
35
36
  ModalManager.__prerenderList__ = [];
37
+ for (const entry of entries)
38
+ try {
39
+ if (entry.dispatch)
40
+ entry.dispatch();
41
+ else
42
+ handler(entry.modal);
43
+ }
44
+ catch {
45
+ }
46
+ }
47
+ static bindPrerender(modal, dispatch) {
48
+ const list = ModalManager.__prerenderList__;
49
+ for (let index = 0; index < list.length; index++)
50
+ if (list[index].modal === modal) {
51
+ list[index].dispatch = dispatch;
52
+ return;
53
+ }
54
+ }
55
+ static cancelPrerender(modal) {
56
+ const list = ModalManager.__prerenderList__;
57
+ for (let index = 0; index < list.length; index++)
58
+ if (list[index].modal === modal) {
59
+ list.splice(index, 1);
60
+ return true;
61
+ }
62
+ return false;
36
63
  }
37
64
  static set refreshHandler(handler) {
38
65
  ModalManager.__refreshHandler__ = handler;
@@ -53,11 +80,10 @@ class ModalManager {
53
80
  static reset() {
54
81
  ModalManager.__anchor__ = null;
55
82
  ModalManager.__prerenderList__ = [];
56
- ModalManager.__openHandler__ = ((modal) => {
57
- ModalManager.__prerenderList__.push(modal);
58
- });
83
+ ModalManager.__openHandler__ = ModalManager.__defaultOpenHandler__;
59
84
  ModalManager.__refreshHandler__ = undefined;
60
85
  destroyScope(ModalManager.__scope__);
86
+ ModalManager.__styleManager__ = styleManagerFactory(ModalManager.__scope__);
61
87
  }
62
88
  static open(modal) {
63
89
  return ModalManager.__openHandler__(modal);
@@ -69,14 +95,17 @@ ModalManager.__hash__ = polynomialHash(ModalManager.__scope__);
69
95
  ModalManager.__styleManager__ = styleManagerFactory(ModalManager.__scope__);
70
96
  ModalManager.__styleSheetDefinition__ = new Map();
71
97
  ModalManager.__prerenderList__ = [];
72
- ModalManager.__openHandler__ = ((modal) => {
73
- ModalManager.__prerenderList__.push(modal);
74
- });
98
+ ModalManager.__defaultOpenHandler__ = (modal) => {
99
+ ModalManager.__prerenderList__.push({ modal });
100
+ return undefined;
101
+ };
102
+ ModalManager.__openHandler__ = ModalManager.__defaultOpenHandler__;
75
103
 
76
- const closeModal = (modalNode, refresh = true) => {
77
- if (modalNode.visible === false)
78
- return;
79
- modalNode.onClose();
104
+ const DEFAULT_Z_INDEX = 1000;
105
+ const DEFAULT_ANIMATION_DURATION = '300ms';
106
+ const DEFAULT_BACKDROP_COLOR = 'rgba(0, 0, 0, 0.5)';
107
+
108
+ const hideModal = (modalNode, refresh = true) => {
80
109
  modalNode.onHide();
81
110
  if (refresh)
82
111
  ModalManager.refresh();
@@ -85,85 +114,108 @@ const closeModal = (modalNode, refresh = true) => {
85
114
  return setTimeout(() => modalNode.onDestroy(), modalNode.duration);
86
115
  };
87
116
 
88
- const subscribeAbortSignal = (modalNode, signal) => {
89
- if (signal === undefined)
90
- return null;
91
- const handleAbort = () => closeModal(modalNode);
92
- signal.addEventListener('abort', handleAbort, { once: true });
93
- return () => signal.removeEventListener('abort', handleAbort);
117
+ const closeModal = (modalNode, refresh = true) => {
118
+ if (modalNode.visible === false)
119
+ return;
120
+ modalNode.onClose();
121
+ return hideModal(modalNode, refresh);
94
122
  };
95
123
 
96
- const alertHandler = (args) => {
97
- const modalNode = ModalManager.open({
98
- ...args,
99
- type: 'alert',
100
- });
101
- const unsubscribe = subscribeAbortSignal(modalNode, args.signal);
124
+ const dispatchModal = (modal, { signal, mapResult, cancelResult }) => {
125
+ let modalNode;
126
+ let cancel = () => { };
102
127
  const promiseHandler = new Promise((resolve, reject) => {
128
+ let cleanupAbort = null;
129
+ let settled = false;
130
+ const settle = (result) => {
131
+ if (settled)
132
+ return;
133
+ settled = true;
134
+ cleanupAbort?.();
135
+ cleanupAbort = null;
136
+ resolve(mapResult(result));
137
+ };
138
+ modal.handleResolve = settle;
139
+ cancel = () => {
140
+ if (ModalManager.cancelPrerender(modal))
141
+ settle(cancelResult());
142
+ };
143
+ const wire = (node) => {
144
+ modalNode = node;
145
+ if (signal === undefined)
146
+ return;
147
+ cleanupAbort?.();
148
+ if (signal.aborted) {
149
+ closeModal(node);
150
+ return;
151
+ }
152
+ const handleAbort = () => closeModal(node);
153
+ signal.addEventListener('abort', handleAbort, { once: true });
154
+ cleanupAbort = () => signal.removeEventListener('abort', handleAbort);
155
+ };
103
156
  try {
104
- modalNode.handleResolve = () => {
105
- unsubscribe?.();
106
- resolve();
107
- };
108
- if (args.signal?.aborted)
109
- closeModal(modalNode);
157
+ if (signal?.aborted) {
158
+ settle(cancelResult());
159
+ return;
160
+ }
161
+ const node = ModalManager.open(modal);
162
+ if (node)
163
+ wire(node);
164
+ else {
165
+ if (signal) {
166
+ const handleAbort = () => {
167
+ ModalManager.cancelPrerender(modal);
168
+ settle(cancelResult());
169
+ };
170
+ signal.addEventListener('abort', handleAbort, { once: true });
171
+ cleanupAbort = () => signal.removeEventListener('abort', handleAbort);
172
+ }
173
+ ModalManager.bindPrerender(modal, () => {
174
+ try {
175
+ const flushed = ModalManager.open(modal);
176
+ if (flushed)
177
+ wire(flushed);
178
+ }
179
+ catch (error) {
180
+ settled = true;
181
+ cleanupAbort?.();
182
+ cleanupAbort = null;
183
+ reject(error);
184
+ }
185
+ });
186
+ }
110
187
  }
111
188
  catch (error) {
112
- closeModal(modalNode);
113
- unsubscribe?.();
189
+ cleanupAbort?.();
114
190
  reject(error);
115
191
  }
116
192
  });
117
- return { modalNode, promiseHandler };
193
+ return {
194
+ get modalNode() {
195
+ return modalNode;
196
+ },
197
+ promiseHandler,
198
+ cancel,
199
+ };
118
200
  };
119
201
 
120
- const confirmHandler = (args) => {
121
- const modalNode = ModalManager.open({
122
- ...args,
123
- type: 'confirm',
124
- });
125
- const unsubscribe = subscribeAbortSignal(modalNode, args.signal);
126
- const promiseHandler = new Promise((resolve, reject) => {
127
- try {
128
- modalNode.handleResolve = (result) => {
129
- unsubscribe?.();
130
- resolve(result ?? false);
131
- };
132
- if (args.signal?.aborted)
133
- closeModal(modalNode);
134
- }
135
- catch (error) {
136
- closeModal(modalNode);
137
- unsubscribe?.();
138
- reject(error);
139
- }
140
- });
141
- return { modalNode, promiseHandler };
142
- };
202
+ const alertHandler = (args) => dispatchModal({ ...args, type: 'alert' }, {
203
+ signal: args.signal,
204
+ mapResult: () => undefined,
205
+ cancelResult: () => null,
206
+ });
143
207
 
144
- const promptHandler = (args) => {
145
- const modalNode = ModalManager.open({
146
- ...args,
147
- type: 'prompt',
148
- });
149
- const unsubscribe = subscribeAbortSignal(modalNode, args.signal);
150
- const promiseHandler = new Promise((resolve, reject) => {
151
- try {
152
- modalNode.handleResolve = (result) => {
153
- unsubscribe?.();
154
- resolve(result);
155
- };
156
- if (args.signal?.aborted)
157
- closeModal(modalNode);
158
- }
159
- catch (error) {
160
- closeModal(modalNode);
161
- unsubscribe?.();
162
- reject(error);
163
- }
164
- });
165
- return { modalNode, promiseHandler };
166
- };
208
+ const confirmHandler = (args) => dispatchModal({ ...args, type: 'confirm' }, {
209
+ signal: args.signal,
210
+ mapResult: (result) => result ?? false,
211
+ cancelResult: () => null,
212
+ });
213
+
214
+ const promptHandler = (args) => dispatchModal({ ...args, type: 'prompt' }, {
215
+ signal: args.signal,
216
+ mapResult: (result) => result ?? null,
217
+ cancelResult: () => args.returnOnCancel ? (args.defaultValue ?? null) : null,
218
+ });
167
219
 
168
220
  const alert = (args) => alertHandler(args).promiseHandler;
169
221
  const confirm = (args) => confirmHandler(args).promiseHandler;
@@ -342,10 +394,6 @@ const nodeFactory = (modal) => {
342
394
  throw new Error(`Unknown modal: ${modal.type}`, { modal });
343
395
  };
344
396
 
345
- const DEFAULT_Z_INDEX = 1000;
346
- const DEFAULT_ANIMATION_DURATION = '300ms';
347
- const DEFAULT_BACKDROP_COLOR = 'rgba(0, 0, 0, 0.5)';
348
-
349
397
  const FallbackTitle = ({ children }) => {
350
398
  return jsx("h2", { style: { margin: 'unset' }, children: children });
351
399
  };
@@ -478,24 +526,12 @@ const ModalManagerContextProvider = memo(({ usePathname, children, }) => {
478
526
  const initiator = useRef(pathname);
479
527
  const modalIdSequence = useRef(0);
480
528
  const options = useConfigurationOptions();
481
- const duration = useMemo(() => convertMsFromDuration(options.duration), [options]);
529
+ const optionsRef = useReference(options);
482
530
  useOnMountLayout(() => {
483
- const { manualDestroy, closeOnBackdropClick } = options;
484
- for (const data of ModalManager.prerender) {
485
- const modal = nodeFactory({
486
- duration,
487
- manualDestroy,
488
- closeOnBackdropClick,
489
- ...data,
490
- id: modalIdSequence.current++,
491
- initiator: initiator.current,
492
- });
493
- modalDictionary.current.set(modal.id, modal);
494
- setModalIds((ids) => [...ids, modal.id]);
495
- }
496
531
  ModalManager.openHandler = (data) => {
532
+ const { duration, manualDestroy, closeOnBackdropClick } = optionsRef.current;
497
533
  const modalNode = nodeFactory({
498
- duration,
534
+ duration: convertMsFromDuration(duration),
499
535
  manualDestroy,
500
536
  closeOnBackdropClick,
501
537
  ...data,
@@ -530,43 +566,33 @@ const ModalManagerContextProvider = memo(({ usePathname, children, }) => {
530
566
  }
531
567
  initiator.current = pathname;
532
568
  }, [pathname]);
533
- const getModalNodeRef = useRef((modalId) => modalDictionary.current.get(modalId));
569
+ const getModalNodeRef = useRef((modalId) => modalId !== undefined ? modalDictionary.current.get(modalId) : undefined);
534
570
  const onDestroyRef = useRef((modalId) => {
535
- const modal = modalDictionary.current.get(modalId);
571
+ const modal = getModalNodeRef.current(modalId);
536
572
  if (!modal)
537
573
  return;
538
574
  modal.onDestroy();
539
575
  ModalManager.refresh();
540
576
  });
541
- const hideModalRef = useRef((modalId) => {
542
- const modal = modalDictionary.current.get(modalId);
543
- if (!modal)
544
- return;
545
- modal.onHide();
546
- ModalManager.refresh();
547
- if (modal.manualDestroy === false)
548
- setTimeout(() => modal.onDestroy(), modal.duration);
549
- });
550
577
  const onChangeRef = useRef((modalId, value) => {
551
- const modal = modalDictionary.current.get(modalId);
578
+ const modal = getModalNodeRef.current(modalId);
552
579
  if (!modal)
553
580
  return;
554
581
  if (modal.type === 'prompt')
555
582
  modal.onChange(value);
556
583
  });
557
584
  const onConfirmRef = useRef((modalId) => {
558
- const modal = modalDictionary.current.get(modalId);
585
+ const modal = getModalNodeRef.current(modalId);
559
586
  if (!modal)
560
587
  return;
561
588
  modal.onConfirm();
562
- hideModalRef.current(modalId);
589
+ hideModal(modal);
563
590
  });
564
591
  const onCloseRef = useRef((modalId) => {
565
- const modal = modalDictionary.current.get(modalId);
592
+ const modal = getModalNodeRef.current(modalId);
566
593
  if (!modal)
567
594
  return;
568
- modal.onClose();
569
- hideModalRef.current(modalId);
595
+ closeModal(modal);
570
596
  });
571
597
  const getModalRef = useRef((modalId) => ({
572
598
  modal: getModalNodeRef.current(modalId),
@@ -603,21 +629,26 @@ const useUserDefinedContext = () => {
603
629
  const usePathname = () => {
604
630
  const [pathname, setPathname] = useState(window.location.pathname);
605
631
  useLayoutEffect(() => {
632
+ const handleChange = () => setPathname(window.location.pathname);
633
+ const navigation = window.navigation;
634
+ if (navigation) {
635
+ navigation.addEventListener('currententrychange', handleChange);
636
+ window.addEventListener('popstate', handleChange);
637
+ return () => {
638
+ navigation.removeEventListener('currententrychange', handleChange);
639
+ window.removeEventListener('popstate', handleChange);
640
+ };
641
+ }
606
642
  let requestId;
607
643
  const checkPathname = () => {
608
- if (requestId)
609
- cancelAnimationFrame(requestId);
610
- if (pathname !== window.location.pathname) {
644
+ if (pathname !== window.location.pathname)
611
645
  setPathname(window.location.pathname);
612
- }
613
- else {
646
+ else
614
647
  requestId = requestAnimationFrame(checkPathname);
615
- }
616
648
  };
617
649
  requestId = requestAnimationFrame(checkPathname);
618
650
  return () => {
619
- if (requestId)
620
- cancelAnimationFrame(requestId);
651
+ cancelAnimationFrame(requestId);
621
652
  };
622
653
  }, [pathname]);
623
654
  return { pathname };
@@ -723,7 +754,7 @@ const PromptInner = memo(({ modal, handlers }) => {
723
754
  const handleConfirm = useCallback(() => {
724
755
  requestAnimationFrame(onConfirm);
725
756
  }, [onConfirm]);
726
- const disabled = useMemo(() => (value ? !!checkDisabled?.(value) : false), [checkDisabled, value]);
757
+ const disabled = useMemo(() => (checkDisabled ? !!checkDisabled(value) : false), [checkDisabled, value]);
727
758
  const { TitleComponent, SubtitleComponent, ContentComponent, FooterComponent, } = useConfigurationContext();
728
759
  return (jsxs(Fragment, { children: [title &&
729
760
  (isString(title) ? (jsx(TitleComponent, { context: userDefinedContext, children: title })) : (title)), subtitle &&
@@ -796,6 +827,7 @@ const style$1 = `
796
827
  z-index: var(--z-index);
797
828
  pointer-events: none;
798
829
  overflow: hidden;
830
+ }
799
831
  `;
800
832
  ModalManager.defineStyleSheet('presenter', style$1);
801
833
 
@@ -854,8 +886,10 @@ const AnchorInner = () => {
854
886
  }, [update]);
855
887
  const options = useConfigurationOptions();
856
888
  const dimmed = useActiveModalCount(validateDimmable, version);
857
- if (!dimmed)
858
- reset();
889
+ useEffect(() => {
890
+ if (!dimmed)
891
+ reset();
892
+ }, [dimmed]);
859
893
  const anchorStyle = useMemo(() => ({
860
894
  '--z-index': options.zIndex,
861
895
  '--transition-duration': options.duration,
@@ -896,12 +930,12 @@ const BootstrapProvider = memo(forwardRef(({ usePathname: useExternalPathname, F
896
930
  initialize: handleInitialize,
897
931
  }), [handleInitialize]);
898
932
  useOnMount(() => {
899
- if (handleRef !== null)
900
- return;
901
- handleInitialize();
933
+ if (handleRef === null)
934
+ handleInitialize();
902
935
  return () => {
903
- if (anchorRef.current)
904
- anchorRef.current.remove();
936
+ if (anchorRef.current === null)
937
+ return;
938
+ anchorRef.current.remove();
905
939
  handleReset();
906
940
  };
907
941
  });
@@ -922,13 +956,15 @@ const BootstrapProvider = memo(forwardRef(({ usePathname: useExternalPathname, F
922
956
 
923
957
  const useBootstrap = ({ usePathname: useExternalPathname, ForegroundComponent, BackgroundComponent, TitleComponent, SubtitleComponent, ContentComponent, FooterComponent, options, context, mode = 'auto', } = {}) => {
924
958
  const usePathname$1 = useMemo(() => useExternalPathname || usePathname, [useExternalPathname]);
925
- const { anchorRef, handleInitialize } = useInitialize();
959
+ const { anchorRef, handleInitialize, handleReset } = useInitialize();
926
960
  useOnMount(() => {
927
961
  if (mode === 'auto')
928
962
  handleInitialize();
929
963
  return () => {
930
- if (anchorRef.current)
931
- anchorRef.current.remove();
964
+ if (anchorRef.current === null)
965
+ return;
966
+ anchorRef.current.remove();
967
+ handleReset();
932
968
  };
933
969
  });
934
970
  const initialize = useCallback((element) => {
@@ -952,28 +988,33 @@ const useBootstrap = ({ usePathname: useExternalPathname, ForegroundComponent, B
952
988
  };
953
989
 
954
990
  const useModal = (configuration) => {
955
- const modalNodesRef = useRef([]);
991
+ const modalResultsRef = useRef([]);
956
992
  const baseArgsRef = useRef(configuration);
957
993
  const alertRef = useRef((args) => {
958
- const { modalNode, promiseHandler } = alertHandler(baseArgsRef.current ? { ...baseArgsRef.current, ...args } : args);
959
- modalNodesRef.current.push(modalNode);
960
- return promiseHandler;
994
+ const result = alertHandler(baseArgsRef.current ? { ...baseArgsRef.current, ...args } : args);
995
+ modalResultsRef.current.push(result);
996
+ return result.promiseHandler;
961
997
  });
962
998
  const confirmRef = useRef((args) => {
963
- const { modalNode, promiseHandler } = confirmHandler(baseArgsRef.current ? { ...baseArgsRef.current, ...args } : args);
964
- modalNodesRef.current.push(modalNode);
965
- return promiseHandler;
999
+ const result = confirmHandler(baseArgsRef.current ? { ...baseArgsRef.current, ...args } : args);
1000
+ modalResultsRef.current.push(result);
1001
+ return result.promiseHandler;
966
1002
  });
967
1003
  const promptRef = useRef((args) => {
968
- const { modalNode, promiseHandler } = promptHandler(baseArgsRef.current ? { ...baseArgsRef.current, ...args } : args);
969
- modalNodesRef.current.push(modalNode);
970
- return promiseHandler;
1004
+ const result = promptHandler(baseArgsRef.current ? { ...baseArgsRef.current, ...args } : args);
1005
+ modalResultsRef.current.push(result);
1006
+ return result.promiseHandler;
971
1007
  });
972
1008
  useOnUnmount(() => {
973
- for (const node of modalNodesRef.current)
974
- closeModal(node, false);
1009
+ for (const result of modalResultsRef.current) {
1010
+ const modalNode = result.modalNode;
1011
+ if (modalNode)
1012
+ closeModal(modalNode, false);
1013
+ else
1014
+ result.cancel();
1015
+ }
975
1016
  ModalManager.refresh();
976
- modalNodesRef.current = [];
1017
+ modalResultsRef.current = [];
977
1018
  });
978
1019
  return {
979
1020
  alert: alertRef.current,
@@ -1026,8 +1067,6 @@ const useModalAnimation = (visible, handler) => {
1026
1067
 
1027
1068
  const useModalDuration = (modalId) => {
1028
1069
  const globalDuration = useConfigurationDuration();
1029
- if (modalId === undefined)
1030
- return globalDuration;
1031
1070
  const { modal } = useModalManager(modalId);
1032
1071
  if (modal === undefined)
1033
1072
  return globalDuration;
@@ -3,7 +3,7 @@ import type { ModalNode } from '../../core/index.js';
3
3
  import type { ModalActions, ModalHandlersWithId } from '../../types/index.js';
4
4
  export interface ModalManagerContextProps extends ModalHandlersWithId {
5
5
  modalIds: ModalNode['id'][];
6
- getModal: Fn<[id: ModalNode['id']], ModalActions>;
7
- getModalNode: Fn<[id: ModalNode['id']], ModalNode | undefined>;
6
+ getModal: Fn<[id: ModalNode['id'] | undefined], ModalActions>;
7
+ getModalNode: Fn<[id: ModalNode['id'] | undefined], ModalNode | undefined>;
8
8
  }
9
9
  export declare const ModalManagerContext: import("react").Context<ModalManagerContextProps>;
@@ -1,3 +1,3 @@
1
1
  import type { ManagedModal } from '../../types/index.js';
2
2
  export declare const useModalManagerContext: () => import("./ModalManagerContext").ModalManagerContextProps;
3
- export declare const useModalManager: (id: ManagedModal["id"]) => import("../../types").ModalActions;
3
+ export declare const useModalManager: (id?: ManagedModal["id"]) => import("../../types").ModalActions;
@@ -11,6 +11,11 @@ export interface BaseModal<T, B> {
11
11
  duration?: number;
12
12
  manualDestroy?: boolean;
13
13
  closeOnBackdropClick?: boolean;
14
+ /**
15
+ * @internal Promise settlement channel injected by the handle layer
16
+ * (dispatchModal). Travels with the modal data so prerendered modals keep
17
+ * their wiring; not part of the public props surface.
18
+ */
14
19
  handleResolve?: Fn<[result: T | null]>;
15
20
  ForegroundComponent?: ForegroundComponent;
16
21
  BackgroundComponent?: BackgroundComponent;
@@ -118,10 +118,10 @@ const userInfo = await prompt<{ name: string; age: number }>({
118
118
  |------|------|------|
119
119
  | `Input` | `(props: PromptInputProps<T>) => ReactNode` | Input component (required) |
120
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 |
121
+ | `disabled` | `(value: T | undefined) => boolean` | Condition to disable confirm button |
122
+ | `returnOnCancel` | `boolean` | If true, cancel resolves with the current input value instead of null |
123
123
 
124
- **Return Value**: `Promise<T>`
124
+ **Return Value**: `Promise<T | null>` — resolves null on cancel
125
125
 
126
126
  ---
127
127
 
@@ -311,7 +311,7 @@ Opens a prompt modal to collect user input.
311
311
  #### Signature
312
312
 
313
313
  ```typescript
314
- function prompt<T, B = any>(options: PromptProps<T, B>): Promise<T>;
314
+ function prompt<T, B = any>(options: PromptProps<T, B>): Promise<T | null>;
315
315
  ```
316
316
 
317
317
  #### Parameters
@@ -322,8 +322,8 @@ All options from `alert`, plus:
322
322
  |--------|------|----------|-------------|
323
323
  | `Input` | `(props: PromptInputProps<T>) => ReactNode` | Yes | Input component |
324
324
  | `defaultValue` | `T` | No | Default value |
325
- | `disabled` | `(value: T) => boolean` | No | Disable confirm |
326
- | `returnOnCancel` | `boolean` | No | Return default on cancel |
325
+ | `disabled` | `(value: T | undefined) => boolean` | No | Disable confirm |
326
+ | `returnOnCancel` | `boolean` | No | Resolve current value on cancel (instead of null) |
327
327
 
328
328
  #### PromptInputProps
329
329
 
@@ -311,7 +311,7 @@ if (shouldDelete) {
311
311
  #### 시그니처
312
312
 
313
313
  ```typescript
314
- function prompt<T, B = any>(options: PromptProps<T, B>): Promise<T>;
314
+ function prompt<T, B = any>(options: PromptProps<T, B>): Promise<T | null>;
315
315
  ```
316
316
 
317
317
  #### 매개변수
@@ -322,8 +322,8 @@ function prompt<T, B = any>(options: PromptProps<T, B>): Promise<T>;
322
322
  |------|------|------|------|
323
323
  | `Input` | `(props: PromptInputProps<T>) => ReactNode` | 예 | 입력 컴포넌트 |
324
324
  | `defaultValue` | `T` | 아니오 | 기본값 |
325
- | `disabled` | `(value: T) => boolean` | 아니오 | 확인 버튼 비활성화 |
326
- | `returnOnCancel` | `boolean` | 아니오 | 취소 시 기본값 반환 |
325
+ | `disabled` | `(value: T | undefined) => boolean` | 아니오 | 확인 버튼 비활성화 |
326
+ | `returnOnCancel` | `boolean` | 아니오 | 취소 시 null 대신 현재 입력값 반환 |
327
327
 
328
328
  #### PromptInputProps
329
329
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lerx/promise-modal",
3
- "version": "0.12.2",
3
+ "version": "0.12.3",
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",
@@ -29,7 +29,11 @@
29
29
  "name": "Vincent K. Kelvin",
30
30
  "email": "lunox273@gmail.com"
31
31
  },
32
- "sideEffects": false,
32
+ "sideEffects": [
33
+ "./dist/index.mjs",
34
+ "./dist/index.cjs",
35
+ "**/style.ts"
36
+ ],
33
37
  "type": "module",
34
38
  "exports": {
35
39
  ".": {
@@ -61,23 +65,30 @@
61
65
  "size-limit": "size-limit",
62
66
  "start": "yarn build && yarn storybook",
63
67
  "storybook": "storybook dev -p 6006",
64
- "test": "vitest",
68
+ "test": "yarn run -T vitest",
69
+ "test:prod": "yarn run -T vitest run --config vitest.prod.config.ts",
65
70
  "version:major": "yarn version major",
66
71
  "version:minor": "yarn version minor",
67
72
  "version:patch": "yarn version patch"
68
73
  },
69
74
  "dependencies": {
70
- "@winglet/common-utils": "^0.12.2",
75
+ "@winglet/common-utils": "^0.12.3",
71
76
  "@winglet/react-utils": "^0.12.2",
72
77
  "@winglet/style-utils": "^0.12.2"
73
78
  },
74
79
  "devDependencies": {
75
- "@slats/claude-assets-sync": "^0.3.1",
80
+ "@slats/claude-assets-sync": "^0.3.4",
81
+ "@testing-library/dom": "^10.4.0",
82
+ "@testing-library/react": "^16.1.0",
76
83
  "@types/react": "^19.0.0",
77
84
  "@types/react-dom": "^19.0.0",
78
85
  "antd": "^5.22.5",
79
86
  "react": "^19.0.0",
80
- "react-dom": "^19.0.0"
87
+ "react-dom": "^19.0.0",
88
+ "rollup": "^4.59.0",
89
+ "rollup-plugin-peer-deps-external": "^2.2.4",
90
+ "tsc-alias": "^1.8.16",
91
+ "typescript": "^5.7.2"
81
92
  },
82
93
  "peerDependencies": {
83
94
  "react": ">=18 <20",
@@ -1,2 +0,0 @@
1
- import type { ModalNode } from '../core/index.js';
2
- export declare const subscribeAbortSignal: (modalNode: ModalNode, signal: AbortSignal | undefined) => (() => void) | null;