@lerx/promise-modal 0.2.2 → 0.2.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.
@@ -13,6 +13,7 @@ export declare class ModalManager {
13
13
  static get unanchored(): boolean;
14
14
  static defineStyleSheet(styleId: string, css: string): void;
15
15
  static applyStyleSheet(): void;
16
+ static getHashedClassNames(styleId: string): string;
16
17
  static reset(): void;
17
18
  static open(modal: Modal): void;
18
19
  }
@@ -1 +1 @@
1
- export declare const style = "\n[data-anchor] {\n display: flex;\n align-items: center;\n justify-content: center;\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 1000;\n transition: background-color ease-in-out;\n}";
1
+ export declare const anchor: string;
@@ -1 +1,3 @@
1
- export declare const style = "\n[data-background] {\n display: none;\n position: fixed;\n inset: 0;\n z-index: -999;\n pointer-events: none;\n}\n\n[data-background] > * {\n pointer-events: none;\n}\n\n[data-background][data-active] {\n pointer-events: all;\n}\n\n[data-background][data-visible] {\n display: flex;\n align-items: center;\n justify-content: center;\n}\n";
1
+ export declare const background: string;
2
+ export declare const active: string;
3
+ export declare const visible: string;
@@ -1 +1,3 @@
1
- export declare const style = "\n[data-foreground] {\n pointer-events: none;\n display: none;\n position: fixed;\n inset: 0;\n z-index: 1;\n}\n\n[data-foreground][data-active] > * {\n pointer-events: all;\n}\n\n[data-foreground][data-visible] {\n display: flex !important;\n justify-content: center;\n align-items: center;\n}\n";
1
+ export declare const foreground: string;
2
+ export declare const active: string;
3
+ export declare const visible: string;
@@ -1 +1 @@
1
- export declare const style = "\n[data-presenter] {\n position: fixed;\n inset: 0;\n pointer-events: none;\n overflow: hidden;\n";
1
+ export declare const presenter: string;
@@ -1,6 +1,7 @@
1
1
  import type { ComponentType, ReactNode } from 'react';
2
2
  import type { AlertContentProps, AlertFooterRender, BackgroundComponent, FooterOptions, ForegroundComponent, ModalBackground } from '../../types';
3
3
  interface AlertProps<B> {
4
+ group?: string;
4
5
  subtype?: 'info' | 'success' | 'warning' | 'error';
5
6
  title?: ReactNode;
6
7
  subtitle?: ReactNode;
@@ -13,5 +14,5 @@ interface AlertProps<B> {
13
14
  ForegroundComponent?: ForegroundComponent;
14
15
  BackgroundComponent?: BackgroundComponent;
15
16
  }
16
- export declare const alert: <B = any>({ subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }: AlertProps<B>) => Promise<void>;
17
+ export declare const alert: <B = any>({ group, subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }: AlertProps<B>) => Promise<void>;
17
18
  export {};
@@ -1,6 +1,7 @@
1
1
  import type { ComponentType, ReactNode } from 'react';
2
2
  import type { BackgroundComponent, ConfirmContentProps, ConfirmFooterRender, FooterOptions, ForegroundComponent, ModalBackground } from '../../types';
3
3
  interface ConfirmProps<B> {
4
+ group?: string;
4
5
  subtype?: 'info' | 'success' | 'warning' | 'error';
5
6
  title?: ReactNode;
6
7
  subtitle?: ReactNode;
@@ -13,5 +14,5 @@ interface ConfirmProps<B> {
13
14
  ForegroundComponent?: ForegroundComponent;
14
15
  BackgroundComponent?: BackgroundComponent;
15
16
  }
16
- export declare const confirm: <B = any>({ subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }: ConfirmProps<B>) => Promise<boolean>;
17
+ export declare const confirm: <B = any>({ group, subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }: ConfirmProps<B>) => Promise<boolean>;
17
18
  export {};
@@ -1,11 +1,12 @@
1
1
  import type { ComponentType, ReactNode } from 'react';
2
2
  import type { BackgroundComponent, FooterOptions, ForegroundComponent, ModalBackground, PromptContentProps, PromptFooterRender, PromptInputProps } from '../../types';
3
3
  interface PromptProps<T, B = any> {
4
+ group?: string;
4
5
  title?: ReactNode;
5
6
  subtitle?: ReactNode;
6
7
  content?: ReactNode | ComponentType<PromptContentProps>;
7
- Input: (props: PromptInputProps<T>) => ReactNode;
8
8
  defaultValue?: T;
9
+ Input: (props: PromptInputProps<T>) => ReactNode;
9
10
  disabled?: (value: T) => boolean;
10
11
  returnOnCancel?: boolean;
11
12
  background?: ModalBackground<B>;
@@ -16,5 +17,5 @@ interface PromptProps<T, B = any> {
16
17
  ForegroundComponent?: ForegroundComponent;
17
18
  BackgroundComponent?: BackgroundComponent;
18
19
  }
19
- export declare const prompt: <T, B = any>({ defaultValue, title, subtitle, content, Input, disabled, returnOnCancel, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }: PromptProps<T, B>) => Promise<T>;
20
+ export declare const prompt: <T, B = any>({ group, title, subtitle, content, defaultValue, Input, disabled, returnOnCancel, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }: PromptProps<T, B>) => Promise<T>;
20
21
  export {};
@@ -5,6 +5,7 @@ type AbstractNodeProps<T, B> = BaseModal<T, B> & ManagedEntity;
5
5
  export declare abstract class AbstractNode<T, B> {
6
6
  #private;
7
7
  readonly id: number;
8
+ readonly group?: string;
8
9
  readonly initiator: string;
9
10
  readonly title?: ReactNode;
10
11
  readonly subtitle?: ReactNode;
@@ -16,7 +17,7 @@ export declare abstract class AbstractNode<T, B> {
16
17
  readonly BackgroundComponent?: BackgroundComponent;
17
18
  get alive(): boolean;
18
19
  get visible(): boolean;
19
- constructor({ id, initiator, title, subtitle, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }: AbstractNodeProps<T, B>);
20
+ constructor({ id, initiator, group, title, subtitle, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }: AbstractNodeProps<T, B>);
20
21
  subscribe(listener: Fn): () => void;
21
22
  publish(): void;
22
23
  protected resolve(result: T | null): void;
@@ -7,7 +7,7 @@ export declare class AlertNode<B> extends AbstractNode<null, B> {
7
7
  readonly subtype?: 'info' | 'success' | 'warning' | 'error';
8
8
  readonly content?: ReactNode | ComponentType<AlertContentProps>;
9
9
  readonly footer?: AlertFooterRender | Pick<FooterOptions, 'confirm' | 'hideConfirm'> | false;
10
- constructor({ id, initiator, type, subtype, title, subtitle, content, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }: AlertNodeProps<B>);
10
+ constructor({ id, group, initiator, type, subtype, title, subtitle, content, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }: AlertNodeProps<B>);
11
11
  onClose(): void;
12
12
  onConfirm(): void;
13
13
  }
@@ -7,7 +7,7 @@ export declare class ConfirmNode<B> extends AbstractNode<boolean, B> {
7
7
  readonly subtype?: 'info' | 'success' | 'warning' | 'error';
8
8
  readonly content?: ReactNode | ComponentType<ConfirmContentProps>;
9
9
  readonly footer?: ConfirmFooterRender | FooterOptions | false;
10
- constructor({ id, initiator, type, subtype, title, subtitle, content, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }: ConfirmNodeProps<B>);
10
+ constructor({ id, group, initiator, type, subtype, title, subtitle, content, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }: ConfirmNodeProps<B>);
11
11
  onClose(): void;
12
12
  onConfirm(): void;
13
13
  }
@@ -11,7 +11,7 @@ export declare class PromptNode<T, B> extends AbstractNode<T, B> {
11
11
  readonly disabled?: (value: T) => boolean;
12
12
  readonly returnOnCancel?: boolean;
13
13
  readonly footer?: PromptFooterRender<T> | FooterOptions | false;
14
- constructor({ id, initiator, type, title, subtitle, content, defaultValue, Input, disabled, returnOnCancel, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }: PromptNodeProps<T, B>);
14
+ constructor({ id, group, initiator, type, title, subtitle, content, defaultValue, Input, disabled, returnOnCancel, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }: PromptNodeProps<T, B>);
15
15
  onChange(value: T): void;
16
16
  onConfirm(): void;
17
17
  onClose(): void;
@@ -1,3 +1,3 @@
1
1
  import type { Fn } from '../@aileron/declare';
2
2
  import type { ModalNode } from '../core';
3
- export declare const useActiveModalCount: (validate?: Fn<[ModalNode?], boolean | undefined>, refreshKey?: string | number) => number;
3
+ export declare const useActiveModalCount: (validate?: Fn<[node?: ModalNode], boolean | undefined>, refreshKey?: string | number) => number;
package/dist/index.cjs CHANGED
@@ -4,11 +4,14 @@ const jsxRuntime = require('react/jsx-runtime');
4
4
  const react = require('react');
5
5
  const convert = require('@winglet/common-utils/convert');
6
6
  const hook = require('@winglet/react-utils/hook');
7
+ const hash = require('@winglet/common-utils/hash');
7
8
  const lib = require('@winglet/common-utils/lib');
8
- const styleManager = require('@winglet/react-utils/style-manager');
9
+ const compressCss = require('@winglet/style-utils/compressCss');
10
+ const styleManager = require('@winglet/style-utils/styleManager');
9
11
  const reactDom = require('react-dom');
10
12
  const array = require('@winglet/common-utils/array');
11
13
  const hoc = require('@winglet/react-utils/hoc');
14
+ const styleUtils = require('@winglet/style-utils');
12
15
  const filter = require('@winglet/common-utils/filter');
13
16
  const render = require('@winglet/react-utils/render');
14
17
  const console = require('@winglet/common-utils/console');
@@ -22,13 +25,14 @@ class ModalManager {
22
25
  }
23
26
  static #anchor = null;
24
27
  static #scope = `promise-modal-${lib.getRandomString(36)}`;
28
+ static #hash = hash.polynomialHash(ModalManager.#scope);
25
29
  static anchor(options) {
26
30
  if (ModalManager.#anchor)
27
31
  return ModalManager.#anchor;
28
32
  const { tag = 'div', prefix = 'promise-modal', root = document.body, } = options || {};
29
33
  const node = document.createElement(tag);
30
- node.setAttribute('id', `${prefix}-${lib.getRandomString(36)}`);
31
- node.setAttribute('data-scope', ModalManager.#scope);
34
+ node.id = `${prefix}-${lib.getRandomString(36)}`;
35
+ node.className = ModalManager.#scope;
32
36
  root.appendChild(node);
33
37
  ModalManager.#anchor = node;
34
38
  return node;
@@ -48,12 +52,15 @@ class ModalManager {
48
52
  static #styleManager = styleManager.styleManagerFactory(ModalManager.#scope);
49
53
  static #styleSheetDefinition = new Map();
50
54
  static defineStyleSheet(styleId, css) {
51
- ModalManager.#styleSheetDefinition.set(styleId, styleManager.compressCss(css));
55
+ ModalManager.#styleSheetDefinition.set(styleId, compressCss.compressCss(css));
52
56
  }
53
57
  static applyStyleSheet() {
54
58
  for (const [styleId, css] of ModalManager.#styleSheetDefinition)
55
59
  ModalManager.#styleManager(styleId, css, true);
56
60
  }
61
+ static getHashedClassNames(styleId) {
62
+ return `${styleId}-${ModalManager.#hash}`;
63
+ }
57
64
  static reset() {
58
65
  ModalManager.#active = false;
59
66
  ModalManager.#anchor = null;
@@ -66,11 +73,12 @@ class ModalManager {
66
73
  }
67
74
  }
68
75
 
69
- const alert = ({ subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }) => {
76
+ const alert = ({ group, subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }) => {
70
77
  return new Promise((resolve, reject) => {
71
78
  try {
72
79
  ModalManager.open({
73
80
  type: 'alert',
81
+ group,
74
82
  subtype,
75
83
  resolve: () => resolve(),
76
84
  title,
@@ -91,11 +99,12 @@ const alert = ({ subtype, title, subtitle, content, background, footer, dimmed,
91
99
  });
92
100
  };
93
101
 
94
- const confirm = ({ subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }) => {
102
+ const confirm = ({ group, subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }) => {
95
103
  return new Promise((resolve, reject) => {
96
104
  try {
97
105
  ModalManager.open({
98
106
  type: 'confirm',
107
+ group,
99
108
  subtype,
100
109
  resolve: (result) => resolve(result ?? false),
101
110
  title,
@@ -116,11 +125,12 @@ const confirm = ({ subtype, title, subtitle, content, background, footer, dimmed
116
125
  });
117
126
  };
118
127
 
119
- const prompt = ({ defaultValue, title, subtitle, content, Input, disabled, returnOnCancel, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }) => {
128
+ const prompt = ({ group, title, subtitle, content, defaultValue, Input, disabled, returnOnCancel, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }) => {
120
129
  return new Promise((resolve, reject) => {
121
130
  try {
122
131
  ModalManager.open({
123
132
  type: 'prompt',
133
+ group,
124
134
  resolve: (result) => resolve(result),
125
135
  title,
126
136
  subtitle,
@@ -146,6 +156,7 @@ const prompt = ({ defaultValue, title, subtitle, content, Input, disabled, retur
146
156
 
147
157
  class AbstractNode {
148
158
  id;
159
+ group;
149
160
  initiator;
150
161
  title;
151
162
  subtitle;
@@ -165,8 +176,9 @@ class AbstractNode {
165
176
  }
166
177
  #resolve;
167
178
  #listeners = new Set();
168
- constructor({ id, initiator, title, subtitle, background, dimmed = true, manualDestroy = false, closeOnBackdropClick = true, resolve, ForegroundComponent, BackgroundComponent, }) {
179
+ constructor({ id, initiator, group, title, subtitle, background, dimmed = true, manualDestroy = false, closeOnBackdropClick = true, resolve, ForegroundComponent, BackgroundComponent, }) {
169
180
  this.id = id;
181
+ this.group = group;
170
182
  this.initiator = initiator;
171
183
  this.title = title;
172
184
  this.subtitle = subtitle;
@@ -218,9 +230,10 @@ class AlertNode extends AbstractNode {
218
230
  subtype;
219
231
  content;
220
232
  footer;
221
- constructor({ id, initiator, type, subtype, title, subtitle, content, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }) {
233
+ constructor({ id, group, initiator, type, subtype, title, subtitle, content, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }) {
222
234
  super({
223
235
  id,
236
+ group,
224
237
  initiator,
225
238
  title,
226
239
  subtitle,
@@ -250,9 +263,10 @@ class ConfirmNode extends AbstractNode {
250
263
  subtype;
251
264
  content;
252
265
  footer;
253
- constructor({ id, initiator, type, subtype, title, subtitle, content, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }) {
266
+ constructor({ id, group, initiator, type, subtype, title, subtitle, content, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }) {
254
267
  super({
255
268
  id,
269
+ group,
256
270
  initiator,
257
271
  title,
258
272
  subtitle,
@@ -286,9 +300,10 @@ class PromptNode extends AbstractNode {
286
300
  returnOnCancel;
287
301
  footer;
288
302
  #value;
289
- constructor({ id, initiator, type, title, subtitle, content, defaultValue, Input, disabled, returnOnCancel, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }) {
303
+ constructor({ id, group, initiator, type, title, subtitle, content, defaultValue, Input, disabled, returnOnCancel, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }) {
290
304
  super({
291
305
  id,
306
+ group,
292
307
  initiator,
293
308
  title,
294
309
  subtitle,
@@ -367,7 +382,8 @@ const useActiveModalCount = (validate = defaultValidate, refreshKey = 0) => {
367
382
  const { modalIds, getModalNode } = useModalManagerContext();
368
383
  return react.useMemo(() => {
369
384
  let count = 0;
370
- for (const id of modalIds) {
385
+ for (let index = 0; index < modalIds.length; index++) {
386
+ const id = modalIds[index];
371
387
  if (validate(getModalNode(id)))
372
388
  count++;
373
389
  }
@@ -622,8 +638,11 @@ const usePathname = () => {
622
638
  return { pathname };
623
639
  };
624
640
 
641
+ const background = ModalManager.getHashedClassNames('background');
642
+ const active$1 = ModalManager.getHashedClassNames('background-active');
643
+ const visible$1 = ModalManager.getHashedClassNames('background-visible');
625
644
  const style$3 = `
626
- [data-background] {
645
+ .${background} {
627
646
  display: none;
628
647
  position: fixed;
629
648
  inset: 0;
@@ -631,22 +650,22 @@ const style$3 = `
631
650
  pointer-events: none;
632
651
  }
633
652
 
634
- [data-background] > * {
653
+ .${background} > * {
635
654
  pointer-events: none;
636
655
  }
637
656
 
638
- [data-background][data-active] {
657
+ .${background}.${active$1} {
639
658
  pointer-events: all;
640
659
  }
641
660
 
642
- [data-background][data-visible] {
661
+ .${background}.${visible$1} {
643
662
  display: flex;
644
663
  align-items: center;
645
664
  justify-content: center;
646
665
  }
647
666
  `;
648
-
649
667
  ModalManager.defineStyleSheet('background', style$3);
668
+
650
669
  const BackgroundFrame = ({ modalId, onChangeOrder, }) => {
651
670
  const { BackgroundComponent } = useConfigurationContext();
652
671
  const { context: userDefinedContext } = useUserDefinedContext();
@@ -659,7 +678,10 @@ const BackgroundFrame = ({ modalId, onChangeOrder, }) => {
659
678
  const Background = react.useMemo(() => modal?.BackgroundComponent || BackgroundComponent, [BackgroundComponent, modal]);
660
679
  if (!modal)
661
680
  return null;
662
- return (jsxRuntime.jsx("div", { "data-background": true, "data-active": styleManager.dataCondition(modal.closeOnBackdropClick && modal.visible), "data-visible": styleManager.dataCondition(modal.manualDestroy ? modal.alive : modal.visible), onClick: handleClose, children: Background && (jsxRuntime.jsx(Background, { id: modal.id, type: modal.type, alive: modal.alive, visible: modal.visible, initiator: modal.initiator, manualDestroy: modal.manualDestroy, closeOnBackdropClick: modal.closeOnBackdropClick, background: modal.background, onChange: onChange, onConfirm: onConfirm, onClose: onClose, onDestroy: onDestroy, onChangeOrder: onChangeOrder, context: userDefinedContext })) }));
681
+ return (jsxRuntime.jsx("div", { className: styleUtils.cx(background, {
682
+ [active$1]: modal.closeOnBackdropClick && modal.visible,
683
+ [visible$1]: modal.manualDestroy ? modal.alive : modal.visible,
684
+ }), onClick: handleClose, children: Background && (jsxRuntime.jsx(Background, { id: modal.id, type: modal.type, alive: modal.alive, visible: modal.visible, initiator: modal.initiator, manualDestroy: modal.manualDestroy, closeOnBackdropClick: modal.closeOnBackdropClick, background: modal.background, onChange: onChange, onConfirm: onConfirm, onClose: onClose, onDestroy: onDestroy, onChangeOrder: onChangeOrder, context: userDefinedContext })) }));
663
685
  };
664
686
 
665
687
  const AlertInner = react.memo(({ modal, handlers }) => {
@@ -739,8 +761,11 @@ const PromptInner = react.memo(({ modal, handlers }) => {
739
761
  })) : (jsxRuntime.jsx(FooterComponent, { disabled: disabled, onConfirm: handleConfirm, onCancel: handleClose, confirmLabel: footer?.confirm, cancelLabel: footer?.cancel, hideConfirm: footer?.hideConfirm, hideCancel: footer?.hideCancel, context: userDefinedContext })))] }));
740
762
  });
741
763
 
764
+ const foreground = ModalManager.getHashedClassNames('foreground');
765
+ const active = ModalManager.getHashedClassNames('foreground-active');
766
+ const visible = ModalManager.getHashedClassNames('foreground-visible');
742
767
  const style$2 = `
743
- [data-foreground] {
768
+ .${foreground} {
744
769
  pointer-events: none;
745
770
  display: none;
746
771
  position: fixed;
@@ -748,18 +773,18 @@ const style$2 = `
748
773
  z-index: 1;
749
774
  }
750
775
 
751
- [data-foreground][data-active] > * {
776
+ .${foreground}.${active} > * {
752
777
  pointer-events: all;
753
778
  }
754
779
 
755
- [data-foreground][data-visible] {
780
+ .${foreground}.${visible} {
756
781
  display: flex !important;
757
782
  justify-content: center;
758
783
  align-items: center;
759
784
  }
760
785
  `;
761
-
762
786
  ModalManager.defineStyleSheet('foreground', style$2);
787
+
763
788
  const ForegroundFrame = ({ modalId, onChangeOrder, }) => {
764
789
  const { ForegroundComponent } = useConfigurationContext();
765
790
  const { context: userDefinedContext } = useUserDefinedContext();
@@ -767,7 +792,10 @@ const ForegroundFrame = ({ modalId, onChangeOrder, }) => {
767
792
  const Foreground = react.useMemo(() => modal?.ForegroundComponent || ForegroundComponent, [ForegroundComponent, modal]);
768
793
  if (!modal)
769
794
  return null;
770
- return (jsxRuntime.jsx("div", { "data-foreground": true, "data-active": styleManager.dataCondition(modal.visible), "data-visible": styleManager.dataCondition(modal.manualDestroy ? modal.alive : modal.visible), children: jsxRuntime.jsxs(Foreground, { id: modal.id, type: modal.type, alive: modal.alive, visible: modal.visible, initiator: modal.initiator, manualDestroy: modal.manualDestroy, closeOnBackdropClick: modal.closeOnBackdropClick, background: modal.background, onChange: onChange, onConfirm: onConfirm, onClose: onClose, onDestroy: onDestroy, onChangeOrder: onChangeOrder, context: userDefinedContext, children: [modal.type === 'alert' && (jsxRuntime.jsx(AlertInner, { modal: modal, handlers: { onConfirm } })), modal.type === 'confirm' && (jsxRuntime.jsx(ConfirmInner, { modal: modal, handlers: { onConfirm, onClose } })), modal.type === 'prompt' && (jsxRuntime.jsx(PromptInner, { modal: modal, handlers: { onChange, onConfirm, onClose } }))] }) }));
795
+ return (jsxRuntime.jsx("div", { className: styleUtils.cx(foreground, {
796
+ [active]: modal.visible,
797
+ [visible]: modal.manualDestroy ? modal.alive : modal.visible,
798
+ }), children: jsxRuntime.jsxs(Foreground, { id: modal.id, type: modal.type, alive: modal.alive, visible: modal.visible, initiator: modal.initiator, manualDestroy: modal.manualDestroy, closeOnBackdropClick: modal.closeOnBackdropClick, background: modal.background, onChange: onChange, onConfirm: onConfirm, onClose: onClose, onDestroy: onDestroy, onChangeOrder: onChangeOrder, context: userDefinedContext, children: [modal.type === 'alert' && (jsxRuntime.jsx(AlertInner, { modal: modal, handlers: { onConfirm } })), modal.type === 'confirm' && (jsxRuntime.jsx(ConfirmInner, { modal: modal, handlers: { onConfirm, onClose } })), modal.type === 'prompt' && (jsxRuntime.jsx(PromptInner, { modal: modal, handlers: { onChange, onConfirm, onClose } }))] }) }));
771
799
  };
772
800
 
773
801
  const useSubscribeModal = (modal) => {
@@ -781,15 +809,16 @@ const useSubscribeModal = (modal) => {
781
809
  return version;
782
810
  };
783
811
 
812
+ const presenter = ModalManager.getHashedClassNames('presenter');
784
813
  const style$1 = `
785
- [data-presenter] {
814
+ .${presenter} {
786
815
  position: fixed;
787
816
  inset: 0;
788
817
  pointer-events: none;
789
818
  overflow: hidden;
790
819
  `;
791
-
792
820
  ModalManager.defineStyleSheet('presenter', style$1);
821
+
793
822
  const { increment } = lib.counterFactory(1);
794
823
  const Presenter = react.memo(({ modalId }) => {
795
824
  const ref = react.useRef(null);
@@ -800,11 +829,12 @@ const Presenter = react.memo(({ modalId }) => {
800
829
  ref.current.style.zIndex = `${increment()}`;
801
830
  }
802
831
  });
803
- return (jsxRuntime.jsxs("div", { ref: ref, "data-presenter": true, children: [jsxRuntime.jsx(BackgroundFrame, { modalId: modalId, onChangeOrder: handleChangeOrder }), jsxRuntime.jsx(ForegroundFrame, { modalId: modalId, onChangeOrder: handleChangeOrder })] }));
832
+ return (jsxRuntime.jsxs("div", { ref: ref, className: presenter, children: [jsxRuntime.jsx(BackgroundFrame, { modalId: modalId, onChangeOrder: handleChangeOrder }), jsxRuntime.jsx(ForegroundFrame, { modalId: modalId, onChangeOrder: handleChangeOrder })] }));
804
833
  });
805
834
 
835
+ const anchor = ModalManager.getHashedClassNames('anchor');
806
836
  const style = `
807
- [data-anchor] {
837
+ .${anchor} {
808
838
  display: flex;
809
839
  align-items: center;
810
840
  justify-content: center;
@@ -814,8 +844,8 @@ const style = `
814
844
  z-index: 1000;
815
845
  transition: background-color ease-in-out;
816
846
  }`;
817
-
818
847
  ModalManager.defineStyleSheet('anchor', style);
848
+
819
849
  const AnchorInner = () => {
820
850
  const [version, update] = hook.useVersion();
821
851
  const { modalIds, setUpdater } = useModalManagerContext();
@@ -824,7 +854,7 @@ const AnchorInner = () => {
824
854
  }, [setUpdater, update]);
825
855
  const options = useConfigurationOptions();
826
856
  const dimmed = useActiveModalCount(validateDimmable, version);
827
- return (jsxRuntime.jsx("div", { "data-anchor": true, style: {
857
+ return (jsxRuntime.jsx("div", { className: anchor, style: {
828
858
  transitionDuration: options.duration,
829
859
  backgroundColor: dimmed ? options.backdrop : 'transparent',
830
860
  }, children: array.map(modalIds, (id) => (jsxRuntime.jsx(Presenter, { modalId: id }, id))) }));
package/dist/index.mjs CHANGED
@@ -2,11 +2,14 @@ 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 { useReference, useOnMountLayout, useHandle, useVersion, useOnMount } from '@winglet/react-utils/hook';
5
+ import { polynomialHash } from '@winglet/common-utils/hash';
5
6
  import { getRandomString, counterFactory } from '@winglet/common-utils/lib';
6
- import { styleManagerFactory, compressCss, destroyScope, dataCondition } from '@winglet/react-utils/style-manager';
7
+ import { compressCss } from '@winglet/style-utils/compressCss';
8
+ import { styleManagerFactory, destroyScope } from '@winglet/style-utils/styleManager';
7
9
  import { createPortal } from 'react-dom';
8
10
  import { map } from '@winglet/common-utils/array';
9
11
  import { withErrorBoundary } from '@winglet/react-utils/hoc';
12
+ import { cx } from '@winglet/style-utils';
10
13
  import { isString, isFunction } from '@winglet/common-utils/filter';
11
14
  import { renderComponent } from '@winglet/react-utils/render';
12
15
  import { printError } from '@winglet/common-utils/console';
@@ -20,13 +23,14 @@ class ModalManager {
20
23
  }
21
24
  static #anchor = null;
22
25
  static #scope = `promise-modal-${getRandomString(36)}`;
26
+ static #hash = polynomialHash(ModalManager.#scope);
23
27
  static anchor(options) {
24
28
  if (ModalManager.#anchor)
25
29
  return ModalManager.#anchor;
26
30
  const { tag = 'div', prefix = 'promise-modal', root = document.body, } = options || {};
27
31
  const node = document.createElement(tag);
28
- node.setAttribute('id', `${prefix}-${getRandomString(36)}`);
29
- node.setAttribute('data-scope', ModalManager.#scope);
32
+ node.id = `${prefix}-${getRandomString(36)}`;
33
+ node.className = ModalManager.#scope;
30
34
  root.appendChild(node);
31
35
  ModalManager.#anchor = node;
32
36
  return node;
@@ -52,6 +56,9 @@ class ModalManager {
52
56
  for (const [styleId, css] of ModalManager.#styleSheetDefinition)
53
57
  ModalManager.#styleManager(styleId, css, true);
54
58
  }
59
+ static getHashedClassNames(styleId) {
60
+ return `${styleId}-${ModalManager.#hash}`;
61
+ }
55
62
  static reset() {
56
63
  ModalManager.#active = false;
57
64
  ModalManager.#anchor = null;
@@ -64,11 +71,12 @@ class ModalManager {
64
71
  }
65
72
  }
66
73
 
67
- const alert = ({ subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }) => {
74
+ const alert = ({ group, subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }) => {
68
75
  return new Promise((resolve, reject) => {
69
76
  try {
70
77
  ModalManager.open({
71
78
  type: 'alert',
79
+ group,
72
80
  subtype,
73
81
  resolve: () => resolve(),
74
82
  title,
@@ -89,11 +97,12 @@ const alert = ({ subtype, title, subtitle, content, background, footer, dimmed,
89
97
  });
90
98
  };
91
99
 
92
- const confirm = ({ subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }) => {
100
+ const confirm = ({ group, subtype, title, subtitle, content, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }) => {
93
101
  return new Promise((resolve, reject) => {
94
102
  try {
95
103
  ModalManager.open({
96
104
  type: 'confirm',
105
+ group,
97
106
  subtype,
98
107
  resolve: (result) => resolve(result ?? false),
99
108
  title,
@@ -114,11 +123,12 @@ const confirm = ({ subtype, title, subtitle, content, background, footer, dimmed
114
123
  });
115
124
  };
116
125
 
117
- const prompt = ({ defaultValue, title, subtitle, content, Input, disabled, returnOnCancel, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }) => {
126
+ const prompt = ({ group, title, subtitle, content, defaultValue, Input, disabled, returnOnCancel, background, footer, dimmed, manualDestroy, closeOnBackdropClick, ForegroundComponent, BackgroundComponent, }) => {
118
127
  return new Promise((resolve, reject) => {
119
128
  try {
120
129
  ModalManager.open({
121
130
  type: 'prompt',
131
+ group,
122
132
  resolve: (result) => resolve(result),
123
133
  title,
124
134
  subtitle,
@@ -144,6 +154,7 @@ const prompt = ({ defaultValue, title, subtitle, content, Input, disabled, retur
144
154
 
145
155
  class AbstractNode {
146
156
  id;
157
+ group;
147
158
  initiator;
148
159
  title;
149
160
  subtitle;
@@ -163,8 +174,9 @@ class AbstractNode {
163
174
  }
164
175
  #resolve;
165
176
  #listeners = new Set();
166
- constructor({ id, initiator, title, subtitle, background, dimmed = true, manualDestroy = false, closeOnBackdropClick = true, resolve, ForegroundComponent, BackgroundComponent, }) {
177
+ constructor({ id, initiator, group, title, subtitle, background, dimmed = true, manualDestroy = false, closeOnBackdropClick = true, resolve, ForegroundComponent, BackgroundComponent, }) {
167
178
  this.id = id;
179
+ this.group = group;
168
180
  this.initiator = initiator;
169
181
  this.title = title;
170
182
  this.subtitle = subtitle;
@@ -216,9 +228,10 @@ class AlertNode extends AbstractNode {
216
228
  subtype;
217
229
  content;
218
230
  footer;
219
- constructor({ id, initiator, type, subtype, title, subtitle, content, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }) {
231
+ constructor({ id, group, initiator, type, subtype, title, subtitle, content, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }) {
220
232
  super({
221
233
  id,
234
+ group,
222
235
  initiator,
223
236
  title,
224
237
  subtitle,
@@ -248,9 +261,10 @@ class ConfirmNode extends AbstractNode {
248
261
  subtype;
249
262
  content;
250
263
  footer;
251
- constructor({ id, initiator, type, subtype, title, subtitle, content, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }) {
264
+ constructor({ id, group, initiator, type, subtype, title, subtitle, content, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }) {
252
265
  super({
253
266
  id,
267
+ group,
254
268
  initiator,
255
269
  title,
256
270
  subtitle,
@@ -284,9 +298,10 @@ class PromptNode extends AbstractNode {
284
298
  returnOnCancel;
285
299
  footer;
286
300
  #value;
287
- constructor({ id, initiator, type, title, subtitle, content, defaultValue, Input, disabled, returnOnCancel, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }) {
301
+ constructor({ id, group, initiator, type, title, subtitle, content, defaultValue, Input, disabled, returnOnCancel, footer, background, dimmed, manualDestroy, closeOnBackdropClick, resolve, ForegroundComponent, BackgroundComponent, }) {
288
302
  super({
289
303
  id,
304
+ group,
290
305
  initiator,
291
306
  title,
292
307
  subtitle,
@@ -365,7 +380,8 @@ const useActiveModalCount = (validate = defaultValidate, refreshKey = 0) => {
365
380
  const { modalIds, getModalNode } = useModalManagerContext();
366
381
  return useMemo(() => {
367
382
  let count = 0;
368
- for (const id of modalIds) {
383
+ for (let index = 0; index < modalIds.length; index++) {
384
+ const id = modalIds[index];
369
385
  if (validate(getModalNode(id)))
370
386
  count++;
371
387
  }
@@ -620,8 +636,11 @@ const usePathname = () => {
620
636
  return { pathname };
621
637
  };
622
638
 
639
+ const background = ModalManager.getHashedClassNames('background');
640
+ const active$1 = ModalManager.getHashedClassNames('background-active');
641
+ const visible$1 = ModalManager.getHashedClassNames('background-visible');
623
642
  const style$3 = `
624
- [data-background] {
643
+ .${background} {
625
644
  display: none;
626
645
  position: fixed;
627
646
  inset: 0;
@@ -629,22 +648,22 @@ const style$3 = `
629
648
  pointer-events: none;
630
649
  }
631
650
 
632
- [data-background] > * {
651
+ .${background} > * {
633
652
  pointer-events: none;
634
653
  }
635
654
 
636
- [data-background][data-active] {
655
+ .${background}.${active$1} {
637
656
  pointer-events: all;
638
657
  }
639
658
 
640
- [data-background][data-visible] {
659
+ .${background}.${visible$1} {
641
660
  display: flex;
642
661
  align-items: center;
643
662
  justify-content: center;
644
663
  }
645
664
  `;
646
-
647
665
  ModalManager.defineStyleSheet('background', style$3);
666
+
648
667
  const BackgroundFrame = ({ modalId, onChangeOrder, }) => {
649
668
  const { BackgroundComponent } = useConfigurationContext();
650
669
  const { context: userDefinedContext } = useUserDefinedContext();
@@ -657,7 +676,10 @@ const BackgroundFrame = ({ modalId, onChangeOrder, }) => {
657
676
  const Background = useMemo(() => modal?.BackgroundComponent || BackgroundComponent, [BackgroundComponent, modal]);
658
677
  if (!modal)
659
678
  return null;
660
- return (jsx("div", { "data-background": true, "data-active": dataCondition(modal.closeOnBackdropClick && modal.visible), "data-visible": dataCondition(modal.manualDestroy ? modal.alive : modal.visible), onClick: handleClose, children: Background && (jsx(Background, { id: modal.id, type: modal.type, alive: modal.alive, visible: modal.visible, initiator: modal.initiator, manualDestroy: modal.manualDestroy, closeOnBackdropClick: modal.closeOnBackdropClick, background: modal.background, onChange: onChange, onConfirm: onConfirm, onClose: onClose, onDestroy: onDestroy, onChangeOrder: onChangeOrder, context: userDefinedContext })) }));
679
+ return (jsx("div", { className: cx(background, {
680
+ [active$1]: modal.closeOnBackdropClick && modal.visible,
681
+ [visible$1]: modal.manualDestroy ? modal.alive : modal.visible,
682
+ }), onClick: handleClose, children: Background && (jsx(Background, { id: modal.id, type: modal.type, alive: modal.alive, visible: modal.visible, initiator: modal.initiator, manualDestroy: modal.manualDestroy, closeOnBackdropClick: modal.closeOnBackdropClick, background: modal.background, onChange: onChange, onConfirm: onConfirm, onClose: onClose, onDestroy: onDestroy, onChangeOrder: onChangeOrder, context: userDefinedContext })) }));
661
683
  };
662
684
 
663
685
  const AlertInner = memo(({ modal, handlers }) => {
@@ -737,8 +759,11 @@ const PromptInner = memo(({ modal, handlers }) => {
737
759
  })) : (jsx(FooterComponent, { disabled: disabled, onConfirm: handleConfirm, onCancel: handleClose, confirmLabel: footer?.confirm, cancelLabel: footer?.cancel, hideConfirm: footer?.hideConfirm, hideCancel: footer?.hideCancel, context: userDefinedContext })))] }));
738
760
  });
739
761
 
762
+ const foreground = ModalManager.getHashedClassNames('foreground');
763
+ const active = ModalManager.getHashedClassNames('foreground-active');
764
+ const visible = ModalManager.getHashedClassNames('foreground-visible');
740
765
  const style$2 = `
741
- [data-foreground] {
766
+ .${foreground} {
742
767
  pointer-events: none;
743
768
  display: none;
744
769
  position: fixed;
@@ -746,18 +771,18 @@ const style$2 = `
746
771
  z-index: 1;
747
772
  }
748
773
 
749
- [data-foreground][data-active] > * {
774
+ .${foreground}.${active} > * {
750
775
  pointer-events: all;
751
776
  }
752
777
 
753
- [data-foreground][data-visible] {
778
+ .${foreground}.${visible} {
754
779
  display: flex !important;
755
780
  justify-content: center;
756
781
  align-items: center;
757
782
  }
758
783
  `;
759
-
760
784
  ModalManager.defineStyleSheet('foreground', style$2);
785
+
761
786
  const ForegroundFrame = ({ modalId, onChangeOrder, }) => {
762
787
  const { ForegroundComponent } = useConfigurationContext();
763
788
  const { context: userDefinedContext } = useUserDefinedContext();
@@ -765,7 +790,10 @@ const ForegroundFrame = ({ modalId, onChangeOrder, }) => {
765
790
  const Foreground = useMemo(() => modal?.ForegroundComponent || ForegroundComponent, [ForegroundComponent, modal]);
766
791
  if (!modal)
767
792
  return null;
768
- return (jsx("div", { "data-foreground": true, "data-active": dataCondition(modal.visible), "data-visible": dataCondition(modal.manualDestroy ? modal.alive : modal.visible), children: jsxs(Foreground, { id: modal.id, type: modal.type, alive: modal.alive, visible: modal.visible, initiator: modal.initiator, manualDestroy: modal.manualDestroy, closeOnBackdropClick: modal.closeOnBackdropClick, background: modal.background, onChange: onChange, onConfirm: onConfirm, onClose: onClose, onDestroy: onDestroy, onChangeOrder: onChangeOrder, context: userDefinedContext, children: [modal.type === 'alert' && (jsx(AlertInner, { modal: modal, handlers: { onConfirm } })), modal.type === 'confirm' && (jsx(ConfirmInner, { modal: modal, handlers: { onConfirm, onClose } })), modal.type === 'prompt' && (jsx(PromptInner, { modal: modal, handlers: { onChange, onConfirm, onClose } }))] }) }));
793
+ return (jsx("div", { className: cx(foreground, {
794
+ [active]: modal.visible,
795
+ [visible]: modal.manualDestroy ? modal.alive : modal.visible,
796
+ }), children: jsxs(Foreground, { id: modal.id, type: modal.type, alive: modal.alive, visible: modal.visible, initiator: modal.initiator, manualDestroy: modal.manualDestroy, closeOnBackdropClick: modal.closeOnBackdropClick, background: modal.background, onChange: onChange, onConfirm: onConfirm, onClose: onClose, onDestroy: onDestroy, onChangeOrder: onChangeOrder, context: userDefinedContext, children: [modal.type === 'alert' && (jsx(AlertInner, { modal: modal, handlers: { onConfirm } })), modal.type === 'confirm' && (jsx(ConfirmInner, { modal: modal, handlers: { onConfirm, onClose } })), modal.type === 'prompt' && (jsx(PromptInner, { modal: modal, handlers: { onChange, onConfirm, onClose } }))] }) }));
769
797
  };
770
798
 
771
799
  const useSubscribeModal = (modal) => {
@@ -779,15 +807,16 @@ const useSubscribeModal = (modal) => {
779
807
  return version;
780
808
  };
781
809
 
810
+ const presenter = ModalManager.getHashedClassNames('presenter');
782
811
  const style$1 = `
783
- [data-presenter] {
812
+ .${presenter} {
784
813
  position: fixed;
785
814
  inset: 0;
786
815
  pointer-events: none;
787
816
  overflow: hidden;
788
817
  `;
789
-
790
818
  ModalManager.defineStyleSheet('presenter', style$1);
819
+
791
820
  const { increment } = counterFactory(1);
792
821
  const Presenter = memo(({ modalId }) => {
793
822
  const ref = useRef(null);
@@ -798,11 +827,12 @@ const Presenter = memo(({ modalId }) => {
798
827
  ref.current.style.zIndex = `${increment()}`;
799
828
  }
800
829
  });
801
- return (jsxs("div", { ref: ref, "data-presenter": true, children: [jsx(BackgroundFrame, { modalId: modalId, onChangeOrder: handleChangeOrder }), jsx(ForegroundFrame, { modalId: modalId, onChangeOrder: handleChangeOrder })] }));
830
+ return (jsxs("div", { ref: ref, className: presenter, children: [jsx(BackgroundFrame, { modalId: modalId, onChangeOrder: handleChangeOrder }), jsx(ForegroundFrame, { modalId: modalId, onChangeOrder: handleChangeOrder })] }));
802
831
  });
803
832
 
833
+ const anchor = ModalManager.getHashedClassNames('anchor');
804
834
  const style = `
805
- [data-anchor] {
835
+ .${anchor} {
806
836
  display: flex;
807
837
  align-items: center;
808
838
  justify-content: center;
@@ -812,8 +842,8 @@ const style = `
812
842
  z-index: 1000;
813
843
  transition: background-color ease-in-out;
814
844
  }`;
815
-
816
845
  ModalManager.defineStyleSheet('anchor', style);
846
+
817
847
  const AnchorInner = () => {
818
848
  const [version, update] = useVersion();
819
849
  const { modalIds, setUpdater } = useModalManagerContext();
@@ -822,7 +852,7 @@ const AnchorInner = () => {
822
852
  }, [setUpdater, update]);
823
853
  const options = useConfigurationOptions();
824
854
  const dimmed = useActiveModalCount(validateDimmable, version);
825
- return (jsx("div", { "data-anchor": true, style: {
855
+ return (jsx("div", { className: anchor, style: {
826
856
  transitionDuration: options.duration,
827
857
  backgroundColor: dimmed ? options.backdrop : 'transparent',
828
858
  }, children: map(modalIds, (id) => (jsx(Presenter, { modalId: id }, id))) }));
@@ -3,6 +3,7 @@ import type { Dictionary } from '../@aileron/declare';
3
3
  import type { ModalBackground } from './background';
4
4
  import type { ModalFrameProps } from './modal';
5
5
  export interface BaseModal<T, B> {
6
+ group?: string;
6
7
  title?: ReactNode;
7
8
  subtitle?: ReactNode;
8
9
  background?: ModalBackground<B>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lerx/promise-modal",
3
- "version": "0.2.2",
3
+ "version": "0.2.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",
@@ -65,8 +65,9 @@
65
65
  "version:patch": "yarn version patch"
66
66
  },
67
67
  "dependencies": {
68
- "@winglet/common-utils": "^0.2.1",
69
- "@winglet/react-utils": "^0.2.2"
68
+ "@winglet/common-utils": "^0.2.2",
69
+ "@winglet/react-utils": "^0.2.3",
70
+ "@winglet/style-utils": "^0.2.0"
70
71
  },
71
72
  "devDependencies": {
72
73
  "@chromatic-com/storybook": "^3.2.6",