@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.
- package/components.d.ts +10 -0
- package/components.js +10 -0
- package/css/badge.css +21 -3
- package/css/menu.css +113 -0
- package/dialog/dialog.d.ts +3 -2
- package/dialog/dialog.js +2 -2
- package/input/input-element.d.ts +3 -1
- package/input/input-element.js +1 -1
- package/menu/README.md +29 -0
- package/menu/components/menu-item-button.d.ts +17 -0
- package/menu/components/menu-item-button.js +27 -0
- package/menu/components/menu-item.d.ts +12 -0
- package/menu/components/menu-item.js +67 -0
- package/menu/index.d.ts +2 -0
- package/menu/index.js +5 -0
- package/menu/menu.d.ts +15 -0
- package/menu/menu.js +23 -0
- package/menu/menu.spec.d.ts +1 -0
- package/menu/menu.spec.js +9 -0
- package/menu/menu.stories.d.ts +2 -0
- package/menu/menu.stories.js +41 -0
- package/menu/types.d.ts +18 -0
- package/menu/types.js +2 -0
- package/modal/README.md +187 -7
- package/modal/constants.d.ts +2 -0
- package/modal/constants.js +11 -0
- package/modal/index.d.ts +6 -2
- package/modal/index.js +13 -2
- package/modal/modal-layer-config.d.ts +12 -0
- package/modal/modal-layer-config.js +60 -0
- package/modal/modal-provider.d.ts +2 -2
- package/modal/modal-provider.js +68 -26
- package/modal/modal-service.d.ts +19 -4
- package/modal/modal-service.js +46 -10
- package/modal/modal.js +1 -1
- package/modal/modal.stories.d.ts +1 -0
- package/modal/modal.stories.js +53 -8
- package/modal/theme.d.ts +4 -0
- package/modal/types.d.ts +38 -2
- package/package.json +1 -1
- package/popover/README.md +10 -0
- package/popover/popover.d.ts +71 -0
- package/popover/popover.js +123 -0
- package/popover/popover.stories.d.ts +2 -0
- package/popover/popover.stories.js +15 -0
- package/readmes.d.ts +2 -0
- package/readmes.js +46 -42
- package/text-input/text-input.d.ts +3 -1
- package/text-input/text-input.js +1 -1
- package/tw-tokens/tw-colors.d.ts +5 -0
- package/tw-tokens/tw-colors.js +5 -0
- package/tw-tokens/tw-z-index.d.ts +1 -0
- package/tw-tokens/tw-z-index.js +2 -1
- package/types/path-params.d.ts +1 -0
- package/types/path-params.js +2 -0
- 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 {
|
|
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={
|
|
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,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,
|
|
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.
|
|
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, "
|
|
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 {
|
|
3
|
-
export declare const ModalProvider: React.ForwardRefExoticComponent<React.RefAttributes<
|
|
2
|
+
import { ModalStackRef } from "./types";
|
|
3
|
+
export declare const ModalProvider: React.ForwardRefExoticComponent<React.RefAttributes<ModalStackRef>>;
|
package/modal/modal-provider.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
42
|
-
const [
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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";
|
package/modal/modal-service.d.ts
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
import { ModalProviderProps,
|
|
2
|
-
export declare function
|
|
3
|
-
|
|
4
|
-
|
|
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;
|
package/modal/modal-service.js
CHANGED
|
@@ -1,20 +1,56 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
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
|
|
8
|
-
function
|
|
9
|
-
return
|
|
9
|
+
const modalStackRef = (0, react_1.createRef)();
|
|
10
|
+
function getModalStackRef() {
|
|
11
|
+
return modalStackRef;
|
|
10
12
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
}
|
package/modal/modal.stories.d.ts
CHANGED