@uxf/ui 11.100.1 → 11.102.0

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.
Files changed (56) hide show
  1. package/components.d.ts +10 -0
  2. package/components.js +10 -0
  3. package/css/badge.css +21 -3
  4. package/css/menu.css +113 -0
  5. package/dialog/dialog.d.ts +3 -2
  6. package/dialog/dialog.js +2 -2
  7. package/input/input-element.d.ts +3 -1
  8. package/input/input-element.js +1 -1
  9. package/menu/README.md +29 -0
  10. package/menu/components/menu-item-button.d.ts +17 -0
  11. package/menu/components/menu-item-button.js +27 -0
  12. package/menu/components/menu-item.d.ts +12 -0
  13. package/menu/components/menu-item.js +67 -0
  14. package/menu/index.d.ts +2 -0
  15. package/menu/index.js +5 -0
  16. package/menu/menu.d.ts +15 -0
  17. package/menu/menu.js +23 -0
  18. package/menu/menu.spec.d.ts +1 -0
  19. package/menu/menu.spec.js +9 -0
  20. package/menu/menu.stories.d.ts +2 -0
  21. package/menu/menu.stories.js +41 -0
  22. package/menu/types.d.ts +18 -0
  23. package/menu/types.js +2 -0
  24. package/modal/README.md +187 -7
  25. package/modal/constants.d.ts +2 -0
  26. package/modal/constants.js +11 -0
  27. package/modal/index.d.ts +6 -2
  28. package/modal/index.js +13 -2
  29. package/modal/modal-layer-config.d.ts +12 -0
  30. package/modal/modal-layer-config.js +60 -0
  31. package/modal/modal-provider.d.ts +2 -2
  32. package/modal/modal-provider.js +68 -26
  33. package/modal/modal-service.d.ts +19 -4
  34. package/modal/modal-service.js +46 -10
  35. package/modal/modal.js +1 -1
  36. package/modal/modal.stories.d.ts +1 -0
  37. package/modal/modal.stories.js +53 -8
  38. package/modal/theme.d.ts +4 -0
  39. package/modal/types.d.ts +38 -2
  40. package/package.json +1 -1
  41. package/popover/README.md +10 -0
  42. package/popover/popover.d.ts +71 -0
  43. package/popover/popover.js +123 -0
  44. package/popover/popover.stories.d.ts +2 -0
  45. package/popover/popover.stories.js +15 -0
  46. package/readmes.d.ts +2 -0
  47. package/readmes.js +46 -42
  48. package/text-input/text-input.d.ts +3 -1
  49. package/text-input/text-input.js +1 -1
  50. package/tw-tokens/tw-colors.d.ts +5 -0
  51. package/tw-tokens/tw-colors.js +5 -0
  52. package/tw-tokens/tw-z-index.d.ts +1 -0
  53. package/tw-tokens/tw-z-index.js +2 -1
  54. package/types/path-params.d.ts +1 -0
  55. package/types/path-params.js +2 -0
  56. package/utils/tailwind-config.js +6 -0
package/modal/README.md CHANGED
@@ -10,13 +10,13 @@
10
10
 
11
11
  ```tsx
12
12
  import {AppProps} from "next/app";
13
- import {getModalRef, ModalProvider} from "@uxf/ui/modal";
13
+ import {getModalStackRef, ModalProvider} from "@uxf/ui/modal";
14
14
 
15
15
  function App(props: AppProps) {
16
16
  return (
17
17
  <UiContextProvider value={...}>
18
18
  {props.children}
19
- <ModalProvider ref={getModalRef()}/>
19
+ <ModalProvider ref={getModalStackRef()}/>
20
20
  </UiContextProvider>
21
21
  );
22
22
  }
@@ -39,9 +39,6 @@ function Component(props: AppProps) {
39
39
  </div>
40
40
  );
41
41
  }
42
-
43
-
44
-
45
42
  ```
46
43
 
47
44
  ## Implementation Modal
@@ -49,7 +46,7 @@ function Component(props: AppProps) {
49
46
  ```tsx
50
47
  function Component(props: AppProps) {
51
48
  const [isOpen, setIsOpen] = useState(false);
52
-
49
+
53
50
  return (
54
51
  <div>
55
52
  <Button onClick={() => setIsOpen(true)}>Click to open modal</Button>
@@ -59,4 +56,187 @@ function Component(props: AppProps) {
59
56
  </div>
60
57
  );
61
58
  }
62
- ```
59
+ ```
60
+
61
+ ## Layered Modals
62
+
63
+ The modal system supports opening multiple modals simultaneously in different layers. Each layer has its own z-index and stacking order.
64
+
65
+ ### Default Layer Configuration
66
+
67
+ By default, four layers are available:
68
+
69
+ ```typescript
70
+ {
71
+ layers: {
72
+ main: { name: "main", zIndex: 1000 },
73
+ confirmation: { name: "confirmation", zIndex: 1010 },
74
+ alert: { name: "alert", zIndex: 1020 },
75
+ critical: { name: "critical", zIndex: 1030 },
76
+ },
77
+ defaultLayer: "main"
78
+ }
79
+ ```
80
+
81
+ ### Basic Usage with Layers
82
+
83
+ ```tsx
84
+ import { openModal, closeModal, closeAllModals, closeModalsByLayer } from "@uxf/ui/modal";
85
+
86
+ // Open modal in default "main" layer
87
+ openModal({
88
+ children: <DialogPanel>Main content</DialogPanel>
89
+ });
90
+
91
+ // Open modal in specific layer (fully type-safe!)
92
+ openModal({
93
+ children: <DialogPanel>Confirmation dialog</DialogPanel>
94
+ }, {
95
+ layer: "confirmation" // TypeScript autocompletes: "main" | "confirmation" | "alert" | "critical"
96
+ });
97
+
98
+ // Close topmost modal
99
+ closeModal();
100
+
101
+ // Close modal in specific layer
102
+ closeModal("confirmation");
103
+
104
+ // Close all modals in a layer
105
+ closeModalsByLayer("alert");
106
+
107
+ // Close all modals across all layers
108
+ closeAllModals();
109
+ ```
110
+
111
+ ### Stacking Behavior
112
+
113
+ - Modals with higher z-index appear on top of modals with lower z-index
114
+ - Only the topmost modal (highest z-index) responds to backdrop clicks and ESC key
115
+ - Clicking inside a higher-layer modal will NOT close lower-layer modals
116
+ - Each layer can have one modal by default (use `replace: false` option to allow multiple)
117
+
118
+ ## Custom Layer Configuration
119
+
120
+ You can configure custom layers for your project using `ModalLayerConfigProvider`:
121
+
122
+ ```tsx
123
+ import {
124
+ ModalProvider,
125
+ ModalLayerConfigProvider,
126
+ getModalStackRef,
127
+ ModalLayersConfiguration
128
+ } from "@uxf/ui/modal";
129
+
130
+ // Define custom layers
131
+ const customModalLayers: ModalLayersConfiguration = {
132
+ layers: {
133
+ base: { name: "base", zIndex: 1000 },
134
+ form: { name: "form", zIndex: 1100 },
135
+ tooltip: { name: "tooltip", zIndex: 1200 },
136
+ notification: { name: "notification", zIndex: 9000 },
137
+ },
138
+ defaultLayer: "base"
139
+ };
140
+
141
+ // Wrap your app with the config provider
142
+ function App({ children }: { children: React.ReactNode }) {
143
+ return (
144
+ <ModalLayerConfigProvider config={customModalLayers}>
145
+ <ModalProvider ref={getModalStackRef()} />
146
+ {children}
147
+ </ModalLayerConfigProvider>
148
+ );
149
+ }
150
+
151
+ // Use your custom layers
152
+ function MyComponent() {
153
+ return (
154
+ <Button onClick={() =>
155
+ openModal({
156
+ children: <DialogPanel>Form modal</DialogPanel>
157
+ }, {
158
+ layer: "form"
159
+ })
160
+ }>
161
+ Open Form
162
+ </Button>
163
+ );
164
+ }
165
+ ```
166
+
167
+ ### Custom Configuration Guidelines
168
+
169
+ - **name**: Unique identifier for the layer
170
+ - **zIndex**: CSS z-index value (higher = on top). This determines both the visual stacking and logical order.
171
+ - **defaultLayer**: Layer name to use when no layer is specified
172
+
173
+ ### TypeScript Support - Custom Layer Names
174
+
175
+ You can extend the available layer names using TypeScript declaration merging:
176
+
177
+ **Step 1: Extend the interface**
178
+
179
+ ```typescript
180
+ // types/modal.d.ts (in your project)
181
+ import "@uxf/ui/modal";
182
+
183
+ declare module "@uxf/ui/modal" {
184
+ interface ModalLayers {
185
+ // Add your custom layer names
186
+ base: true;
187
+ form: true;
188
+ notification: true;
189
+ tooltip: true;
190
+ }
191
+ }
192
+ ```
193
+
194
+ **Step 2: Configure layers at runtime**
195
+
196
+ ```tsx
197
+ // app/layout.tsx or _app.tsx
198
+ import { ModalLayerConfigProvider, ModalProvider, getModalStackRef } from "@uxf/ui/modal";
199
+
200
+ const customModalLayers = {
201
+ layers: {
202
+ // Default layers (keep or override)
203
+ main: { name: "main", zIndex: 1000 },
204
+ confirmation: { name: "confirmation", zIndex: 1010 },
205
+ alert: { name: "alert", zIndex: 1020 },
206
+ critical: { name: "critical", zIndex: 1030 },
207
+ // Your custom layers
208
+ base: { name: "base", zIndex: 900 },
209
+ form: { name: "form", zIndex: 1100 },
210
+ notification: { name: "notification", zIndex: 9000 },
211
+ tooltip: { name: "tooltip", zIndex: 9999 },
212
+ },
213
+ defaultLayer: "main"
214
+ };
215
+
216
+ <ModalLayerConfigProvider config={customModalLayers}>
217
+ <ModalProvider ref={getModalStackRef()} />
218
+ <App />
219
+ </ModalLayerConfigProvider>
220
+ ```
221
+
222
+ **Step 3: Use with full type safety**
223
+
224
+ ```tsx
225
+ import { openModal } from "@uxf/ui/modal";
226
+
227
+ openModal({
228
+ children: <DialogPanel>Form content</DialogPanel>
229
+ }, {
230
+ layer: "form" // ✅ TypeScript autocompletes and validates!
231
+ // Available: "main" | "confirmation" | "alert" | "critical" | "base" | "form" | "notification" | "tooltip"
232
+ });
233
+ ```
234
+
235
+ ## Backwards Compatibility
236
+
237
+ The layered modal system is fully backwards compatible:
238
+
239
+ - Existing code using `openModal()` without options continues to work
240
+ - `getModalRef()` is aliased to `getModalStackRef()`
241
+ - Default "main" layer is used when no layer is specified
242
+ - Single modal behavior is maintained when only one modal is open
@@ -0,0 +1,2 @@
1
+ import type { ModalLayersConfiguration } from "./types";
2
+ export declare const DEFAULT_MODAL_LAYERS: ModalLayersConfiguration;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_MODAL_LAYERS = void 0;
4
+ const tw_z_index_1 = require("../tw-tokens/tw-z-index");
5
+ exports.DEFAULT_MODAL_LAYERS = {
6
+ layers: {
7
+ main: { name: "main", zIndex: parseInt(tw_z_index_1.twZIndex.modal, 10) },
8
+ confirm: { name: "confirm", zIndex: parseInt(tw_z_index_1.twZIndex.confirm, 10) },
9
+ },
10
+ defaultLayer: "main",
11
+ };
package/modal/index.d.ts CHANGED
@@ -1,4 +1,8 @@
1
+ export { DEFAULT_MODAL_LAYERS } from "./constants";
1
2
  export { Modal } from "./modal";
3
+ export { ModalLayerConfigProvider, useModalLayerConfig } from "./modal-layer-config";
2
4
  export { ModalProvider } from "./modal-provider";
3
- export { closeModal, getModalRef, openModal } from "./modal-service";
4
- export type { ModalProps, ModalProviderProps, ModalRef, ModalVariant } from "./types";
5
+ export { closeAllModals, closeModal, closeModalsByLayer, getModalStackRef, openModal } from "./modal-service";
6
+ export type { ModalInstance, ModalLayerConfig, ModalLayerName, ModalLayersConfiguration, ModalProps, ModalProviderProps, ModalRef, ModalStackRef, ModalVariant, OpenModalOptions, } from "./types";
7
+ /** @deprecated use getModalStackRef */
8
+ export { getModalStackRef as getModalRef } from "./modal-service";
package/modal/index.js CHANGED
@@ -1,11 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.openModal = exports.getModalRef = exports.closeModal = exports.ModalProvider = exports.Modal = void 0;
3
+ exports.getModalRef = exports.openModal = exports.getModalStackRef = exports.closeModalsByLayer = exports.closeModal = exports.closeAllModals = exports.ModalProvider = exports.useModalLayerConfig = exports.ModalLayerConfigProvider = exports.Modal = exports.DEFAULT_MODAL_LAYERS = void 0;
4
+ var constants_1 = require("./constants");
5
+ Object.defineProperty(exports, "DEFAULT_MODAL_LAYERS", { enumerable: true, get: function () { return constants_1.DEFAULT_MODAL_LAYERS; } });
4
6
  var modal_1 = require("./modal");
5
7
  Object.defineProperty(exports, "Modal", { enumerable: true, get: function () { return modal_1.Modal; } });
8
+ var modal_layer_config_1 = require("./modal-layer-config");
9
+ Object.defineProperty(exports, "ModalLayerConfigProvider", { enumerable: true, get: function () { return modal_layer_config_1.ModalLayerConfigProvider; } });
10
+ Object.defineProperty(exports, "useModalLayerConfig", { enumerable: true, get: function () { return modal_layer_config_1.useModalLayerConfig; } });
6
11
  var modal_provider_1 = require("./modal-provider");
7
12
  Object.defineProperty(exports, "ModalProvider", { enumerable: true, get: function () { return modal_provider_1.ModalProvider; } });
8
13
  var modal_service_1 = require("./modal-service");
14
+ Object.defineProperty(exports, "closeAllModals", { enumerable: true, get: function () { return modal_service_1.closeAllModals; } });
9
15
  Object.defineProperty(exports, "closeModal", { enumerable: true, get: function () { return modal_service_1.closeModal; } });
10
- Object.defineProperty(exports, "getModalRef", { enumerable: true, get: function () { return modal_service_1.getModalRef; } });
16
+ Object.defineProperty(exports, "closeModalsByLayer", { enumerable: true, get: function () { return modal_service_1.closeModalsByLayer; } });
17
+ Object.defineProperty(exports, "getModalStackRef", { enumerable: true, get: function () { return modal_service_1.getModalStackRef; } });
11
18
  Object.defineProperty(exports, "openModal", { enumerable: true, get: function () { return modal_service_1.openModal; } });
19
+ // Backwards compatibility: getModalRef is now an alias for getModalStackRef
20
+ /** @deprecated use getModalStackRef */
21
+ var modal_service_2 = require("./modal-service");
22
+ Object.defineProperty(exports, "getModalRef", { enumerable: true, get: function () { return modal_service_2.getModalStackRef; } });
@@ -0,0 +1,12 @@
1
+ import React, { ReactNode } from "react";
2
+ import { ModalLayersConfiguration } from "./types";
3
+ interface ModalLayerConfigContextValue {
4
+ config: ModalLayersConfiguration;
5
+ }
6
+ export interface ModalLayerConfigProviderProps {
7
+ children: ReactNode;
8
+ config?: ModalLayersConfiguration;
9
+ }
10
+ export declare function ModalLayerConfigProvider(props: ModalLayerConfigProviderProps): React.JSX.Element;
11
+ export declare function useModalLayerConfig(): ModalLayerConfigContextValue;
12
+ export {};
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ "use client";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.ModalLayerConfigProvider = ModalLayerConfigProvider;
38
+ exports.useModalLayerConfig = useModalLayerConfig;
39
+ const react_1 = __importStar(require("react"));
40
+ const constants_1 = require("./constants");
41
+ const ModalLayerConfigContext = (0, react_1.createContext)({
42
+ config: constants_1.DEFAULT_MODAL_LAYERS,
43
+ });
44
+ function ModalLayerConfigProvider(props) {
45
+ const value = (0, react_1.useMemo)(() => {
46
+ var _a;
47
+ return ({
48
+ config: (_a = props.config) !== null && _a !== void 0 ? _a : constants_1.DEFAULT_MODAL_LAYERS,
49
+ });
50
+ }, [props.config]);
51
+ return react_1.default.createElement(ModalLayerConfigContext.Provider, { value: value }, props.children);
52
+ }
53
+ function useModalLayerConfig() {
54
+ const context = (0, react_1.useContext)(ModalLayerConfigContext);
55
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
56
+ if (!context) {
57
+ return { config: constants_1.DEFAULT_MODAL_LAYERS };
58
+ }
59
+ return context;
60
+ }
@@ -1,3 +1,3 @@
1
1
  import React from "react";
2
- import { ModalRef } from "./types";
3
- export declare const ModalProvider: React.ForwardRefExoticComponent<React.RefAttributes<ModalRef>>;
2
+ import { ModalStackRef } from "./types";
3
+ export declare const ModalProvider: React.ForwardRefExoticComponent<React.RefAttributes<ModalStackRef>>;
@@ -35,34 +35,76 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  })();
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
37
  exports.ModalProvider = void 0;
38
+ const is_empty_1 = require("@uxf/core/utils/is-empty");
38
39
  const react_1 = __importStar(require("react"));
39
- const dialog_1 = require("../dialog/dialog");
40
+ const modal_1 = require("./modal");
41
+ const modal_layer_config_1 = require("./modal-layer-config");
42
+ let modalIdCounter = 0;
43
+ function generateModalId() {
44
+ return `modal-${++modalIdCounter}-${Date.now()}`;
45
+ }
46
+ function ModalDialogRenderer(props) {
47
+ var _a;
48
+ const onClose = props.onClose;
49
+ const onCloseInstance = props.instance.props.onClose;
50
+ const handleClose = (0, react_1.useCallback)(() => {
51
+ onCloseInstance === null || onCloseInstance === void 0 ? void 0 : onCloseInstance();
52
+ onClose();
53
+ }, [onCloseInstance, onClose]);
54
+ // Only the topmost modal can be closed by backdrop/escape
55
+ // This prevents clicking inside a top modal from closing modals underneath
56
+ const isBackdropCloseDisabled = !props.isTopmost || props.instance.props.isBackdropCloseDisabled;
57
+ const isEscKeyCloseDisabled = !props.isTopmost || props.instance.props.isEscapeKeyCloseDisabled;
58
+ return (react_1.default.createElement(modal_1.Modal, { className: `uxf-modal--layer-${props.instance.layer} ${(_a = props.instance.props.className) !== null && _a !== void 0 ? _a : ""}`, isBackdropCloseDisabled: isBackdropCloseDisabled, isEscKeyCloseDisabled: isEscKeyCloseDisabled, isOpen: true, onClose: handleClose, style: { zIndex: props.instance.zIndex }, variant: props.instance.props.variant }, props.instance.props.children));
59
+ }
40
60
  exports.ModalProvider = (0, react_1.forwardRef)((props, ref) => {
41
- var _a, _b;
42
- const [content, setContent] = (0, react_1.useState)();
43
- const { openDialog, DialogProvider, closeDialog } = (0, dialog_1.useDialog)({
44
- isBackdropCloseDisabled: content === null || content === void 0 ? void 0 : content.isBackdropCloseDisabled,
45
- isEscapeKeyCloseDisabled: content === null || content === void 0 ? void 0 : content.isEscapeKeyCloseDisabled,
46
- onDialogClose: content === null || content === void 0 ? void 0 : content.onClose,
47
- });
48
- const openModalHandler = (modal) => {
49
- openDialog(modal.children);
50
- setContent(modal);
51
- };
52
- const closeModalHandler = () => {
53
- if (content === null || content === void 0 ? void 0 : content.onClose) {
54
- content.onClose();
55
- }
56
- closeDialog();
57
- };
58
- const innerRef = (0, react_1.useRef)({
59
- close: closeModalHandler,
60
- open: openModalHandler,
61
- });
61
+ const { config } = (0, modal_layer_config_1.useModalLayerConfig)();
62
+ const [modalStack, setModalStack] = (0, react_1.useState)([]);
63
+ const openInLayer = (0, react_1.useCallback)((modal, options) => {
64
+ var _a;
65
+ const layerName = (_a = options === null || options === void 0 ? void 0 : options.layer) !== null && _a !== void 0 ? _a : config.defaultLayer;
66
+ const layer = config.layers[config.defaultLayer];
67
+ const modalId = generateModalId();
68
+ const shouldReplace = (options === null || options === void 0 ? void 0 : options.shouldReplace) !== false; // default true
69
+ setModalStack((prev) => {
70
+ let filtered = prev;
71
+ // Replace existing modal in same layer if shouldReplace=true
72
+ if (shouldReplace) {
73
+ filtered = prev.filter((m) => m.layer !== layerName);
74
+ }
75
+ const newModal = {
76
+ id: modalId,
77
+ layer: layerName,
78
+ props: modal,
79
+ zIndex: layer.zIndex,
80
+ };
81
+ // Add and sort by zIndex (lower zIndex first, higher on top)
82
+ return [...filtered, newModal].sort((a, b) => a.zIndex - b.zIndex);
83
+ });
84
+ return modalId;
85
+ }, [config]);
86
+ const closeLayer = (0, react_1.useCallback)((layer) => {
87
+ setModalStack((prev) => prev.filter((m) => m.layer !== layer));
88
+ }, []);
89
+ const closeTopmost = (0, react_1.useCallback)(() => {
90
+ setModalStack((prev) => {
91
+ if ((0, is_empty_1.isEmpty)(prev)) {
92
+ return prev;
93
+ }
94
+ // Remove the modal with highest zIndex
95
+ const maxZIndex = Math.max(...prev.map((m) => m.zIndex));
96
+ return prev.filter((m) => m.zIndex !== maxZIndex);
97
+ });
98
+ }, []);
99
+ const closeAll = (0, react_1.useCallback)(() => {
100
+ setModalStack([]);
101
+ }, []);
62
102
  (0, react_1.useImperativeHandle)(ref, () => ({
63
- close: innerRef.current.close,
64
- open: innerRef.current.open,
65
- }), []);
66
- return (react_1.default.createElement(DialogProvider, { className: `uxf-modal uxf-modal--variant-${(_a = content === null || content === void 0 ? void 0 : content.variant) !== null && _a !== void 0 ? _a : "default"} ${(_b = content === null || content === void 0 ? void 0 : content.className) !== null && _b !== void 0 ? _b : ""}`, variant: content === null || content === void 0 ? void 0 : content.variant }));
103
+ openInLayer,
104
+ closeLayer,
105
+ closeTopmost,
106
+ closeAll,
107
+ }), [openInLayer, closeLayer, closeTopmost, closeAll]);
108
+ return (react_1.default.createElement(react_1.default.Fragment, null, modalStack.map((modalInstance) => (react_1.default.createElement(ModalDialogRenderer, { instance: modalInstance, isTopmost: modalInstance.zIndex === Math.max(...modalStack.map((m) => m.zIndex)), key: modalInstance.id, onClose: () => closeLayer(modalInstance.layer) })))));
67
109
  });
68
110
  exports.ModalProvider.displayName = "UxfUiModalProvider";
@@ -1,4 +1,19 @@
1
- import { ModalProviderProps, ModalRef } from "./types";
2
- export declare function getModalRef(): import("react").RefObject<ModalRef>;
3
- export declare function openModal(modal: ModalProviderProps): void;
4
- export declare function closeModal(): void;
1
+ import { ModalLayerName, ModalProviderProps, ModalStackRef, OpenModalOptions } from "./types";
2
+ export declare function getModalStackRef(): import("react").RefObject<ModalStackRef>;
3
+ /**
4
+ * Opens a modal in the specified layer (or default layer if not specified)
5
+ * @returns Modal instance ID
6
+ */
7
+ export declare function openModal(modal: ModalProviderProps, options?: OpenModalOptions): string;
8
+ /**
9
+ * Closes the topmost modal or a modal in specific layer
10
+ */
11
+ export declare function closeModal(layer?: ModalLayerName): void;
12
+ /**
13
+ * Closes all modals in a specific layer
14
+ */
15
+ export declare function closeModalsByLayer(layer: ModalLayerName): void;
16
+ /**
17
+ * Closes all modals across all layers
18
+ */
19
+ export declare function closeAllModals(): void;
@@ -1,20 +1,56 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getModalRef = getModalRef;
3
+ exports.getModalStackRef = getModalStackRef;
4
4
  exports.openModal = openModal;
5
5
  exports.closeModal = closeModal;
6
+ exports.closeModalsByLayer = closeModalsByLayer;
7
+ exports.closeAllModals = closeAllModals;
6
8
  const react_1 = require("react");
7
- const modalRef = (0, react_1.createRef)();
8
- function getModalRef() {
9
- return modalRef;
9
+ const modalStackRef = (0, react_1.createRef)();
10
+ function getModalStackRef() {
11
+ return modalStackRef;
10
12
  }
11
- function openModal(modal) {
12
- if (modalRef.current) {
13
- modalRef.current.open(modal);
13
+ /**
14
+ * Opens a modal in the specified layer (or default layer if not specified)
15
+ * @returns Modal instance ID
16
+ */
17
+ function openModal(modal, options) {
18
+ if (!modalStackRef.current) {
19
+ // eslint-disable-next-line no-console
20
+ console.warn("ModalProvider not initialized. Make sure ModalProvider is mounted with ref from getModalStackRef()");
21
+ return "";
14
22
  }
23
+ return modalStackRef.current.openInLayer(modal, options);
15
24
  }
16
- function closeModal() {
17
- if (modalRef.current) {
18
- modalRef.current.close();
25
+ /**
26
+ * Closes the topmost modal or a modal in specific layer
27
+ */
28
+ function closeModal(layer) {
29
+ if (!modalStackRef.current) {
30
+ return;
19
31
  }
32
+ if (layer) {
33
+ modalStackRef.current.closeLayer(layer);
34
+ }
35
+ else {
36
+ modalStackRef.current.closeTopmost();
37
+ }
38
+ }
39
+ /**
40
+ * Closes all modals in a specific layer
41
+ */
42
+ function closeModalsByLayer(layer) {
43
+ if (!modalStackRef.current) {
44
+ return;
45
+ }
46
+ modalStackRef.current.closeLayer(layer);
47
+ }
48
+ /**
49
+ * Closes all modals across all layers
50
+ */
51
+ function closeAllModals() {
52
+ if (!modalStackRef.current) {
53
+ return;
54
+ }
55
+ modalStackRef.current.closeAll();
20
56
  }
package/modal/modal.js CHANGED
@@ -28,5 +28,5 @@ function Modal(props) {
28
28
  });
29
29
  const { getFloatingProps } = (0, react_1.useInteractions)([click, role, dismiss]);
30
30
  const latestContext = (0, use_latest_1.useLatest)(context);
31
- return (react_2.default.createElement(dialog_1.Dialog, { className: `uxf-modal uxf-modal--variant-${(_a = props.variant) !== null && _a !== void 0 ? _a : "default"} ${(_b = props.className) !== null && _b !== void 0 ? _b : ""}`, context: latestContext.current, forwardedRef: refs.setFloating, getFloatingProps: getFloatingProps, isOpen: props.isOpen, variant: props.variant }, props.children));
31
+ return (react_2.default.createElement(dialog_1.Dialog, { className: `uxf-modal uxf-modal--variant-${(_a = props.variant) !== null && _a !== void 0 ? _a : "default"} ${(_b = props.className) !== null && _b !== void 0 ? _b : ""}`, context: latestContext.current, forwardedRef: refs.setFloating, getFloatingProps: getFloatingProps, isOpen: props.isOpen, style: props.style, variant: props.variant }, props.children));
32
32
  }
@@ -1,3 +1,4 @@
1
1
  import React from "react";
2
2
  export declare function Default(): React.JSX.Element;
3
3
  export declare function ModalProvider(): React.JSX.Element;
4
+ export declare function ModalProviderWithLayers(): React.JSX.Element;