@uxf/ui 11.101.0 → 11.104.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/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/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 +3 -3
- package/text-input/text-input.d.ts +3 -1
- package/text-input/text-input.js +1 -1
- package/tw-tokens/tw-z-index.d.ts +1 -0
- package/tw-tokens/tw-z-index.js +2 -1
- package/utils/tailwind-config.js +1 -0
package/dialog/dialog.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UseFloatingReturn } from "@floating-ui/react";
|
|
2
2
|
import { DialogVariants } from "@uxf/ui/dialog/theme";
|
|
3
|
-
import React, { FC, HTMLProps, ReactNode, Ref } from "react";
|
|
3
|
+
import React, { CSSProperties, FC, HTMLProps, ReactNode, Ref } from "react";
|
|
4
4
|
type GetFloatingElementProps = (userProps?: HTMLProps<HTMLElement>) => Partial<Record<string, unknown>>;
|
|
5
5
|
export type DialogVariant = keyof DialogVariants;
|
|
6
6
|
export interface DialogProps {
|
|
@@ -10,9 +10,10 @@ export interface DialogProps {
|
|
|
10
10
|
forwardedRef: Ref<HTMLDivElement>;
|
|
11
11
|
getFloatingProps: GetFloatingElementProps;
|
|
12
12
|
isOpen: boolean;
|
|
13
|
+
style?: CSSProperties;
|
|
13
14
|
variant?: DialogVariant;
|
|
14
15
|
}
|
|
15
|
-
type DialogProviderProps = Pick<DialogProps, "variant" | "className" | "children">;
|
|
16
|
+
type DialogProviderProps = Pick<DialogProps, "variant" | "className" | "children" | "style">;
|
|
16
17
|
export declare const Dialog: React.NamedExoticComponent<DialogProps>;
|
|
17
18
|
type DialogCloseHandler = () => void;
|
|
18
19
|
export declare function useDialog(dialogConfig?: {
|
package/dialog/dialog.js
CHANGED
|
@@ -45,7 +45,7 @@ exports.Dialog = (0, react_2.memo)((props) => {
|
|
|
45
45
|
var _a, _b;
|
|
46
46
|
return (react_2.default.createElement(react_1.FloatingPortal, null,
|
|
47
47
|
react_2.default.createElement(show_1.Show, { when: props.isOpen },
|
|
48
|
-
react_2.default.createElement(react_1.FloatingOverlay, { className: `uxf-dialog uxf-dialog__backdrop uxf-dialog--variant-${(_a = props.variant) !== null && _a !== void 0 ? _a : "default"} ${(_b = props.className) !== null && _b !== void 0 ? _b : ""}`, lockScroll: true },
|
|
48
|
+
react_2.default.createElement(react_1.FloatingOverlay, { className: `uxf-dialog uxf-dialog__backdrop uxf-dialog--variant-${(_a = props.variant) !== null && _a !== void 0 ? _a : "default"} ${(_b = props.className) !== null && _b !== void 0 ? _b : ""}`, lockScroll: true, style: props.style },
|
|
49
49
|
react_2.default.createElement(react_1.FloatingFocusManager, { context: props.context, guards: true, initialFocus: 0, modal: true },
|
|
50
50
|
react_2.default.createElement("div", { className: "uxf-dialog__wrapper" },
|
|
51
51
|
react_2.default.createElement("div", { className: "uxf-dialog__body" },
|
|
@@ -78,7 +78,7 @@ function useDialog(dialogConfig) {
|
|
|
78
78
|
const { getReferenceProps, getFloatingProps } = (0, react_1.useInteractions)([click, role, dismiss]);
|
|
79
79
|
const latestContext = (0, use_latest_1.useLatest)(context);
|
|
80
80
|
const DialogProvider = (0, react_2.useCallback)((config) => {
|
|
81
|
-
return (react_2.default.createElement(exports.Dialog, { className: config === null || config === void 0 ? void 0 : config.className, context: latestContext.current, forwardedRef: refs.setFloating, getFloatingProps: getFloatingProps, isOpen: Boolean(content), variant: config === null || config === void 0 ? void 0 : config.variant }, content));
|
|
81
|
+
return (react_2.default.createElement(exports.Dialog, { className: config === null || config === void 0 ? void 0 : config.className, context: latestContext.current, forwardedRef: refs.setFloating, getFloatingProps: getFloatingProps, isOpen: Boolean(content), style: config === null || config === void 0 ? void 0 : config.style, variant: config === null || config === void 0 ? void 0 : config.variant }, content));
|
|
82
82
|
}, [latestContext, refs.setFloating, getFloatingProps, content]);
|
|
83
83
|
return {
|
|
84
84
|
openDialog: setContent,
|
package/input/input-element.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FormControlProps } from "@uxf/ui/types/form-control-props";
|
|
2
|
-
import React, { CSSProperties, KeyboardEventHandler } from "react";
|
|
2
|
+
import React, { ClipboardEventHandler, CSSProperties, InputEventHandler, KeyboardEventHandler } from "react";
|
|
3
3
|
type InputMode = "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | undefined;
|
|
4
4
|
export interface InputElementProps extends FormControlProps<string> {
|
|
5
5
|
"aria-describedby"?: string;
|
|
@@ -9,7 +9,9 @@ export interface InputElementProps extends FormControlProps<string> {
|
|
|
9
9
|
form?: string;
|
|
10
10
|
id?: string;
|
|
11
11
|
inputMode?: InputMode;
|
|
12
|
+
onBeforeInput?: InputEventHandler<HTMLInputElement>;
|
|
12
13
|
onKeyDown?: KeyboardEventHandler<HTMLInputElement>;
|
|
14
|
+
onPaste?: ClipboardEventHandler<HTMLInputElement>;
|
|
13
15
|
placeholder?: string;
|
|
14
16
|
type?: "email" | "number" | "password" | "search" | "tel" | "text" | "url" | "time";
|
|
15
17
|
maxLength?: number | undefined;
|
package/input/input-element.js
CHANGED
|
@@ -39,6 +39,6 @@ const react_1 = __importStar(require("react"));
|
|
|
39
39
|
exports.InputElement = (0, react_1.forwardRef)((props, ref) => {
|
|
40
40
|
const onChange = (event) => props.onChange(event.target.value, event);
|
|
41
41
|
const ariaAndDataAttrs = (0, filter_aria_and_data_attrs_1.filterAriaAndDataAttrs)(props);
|
|
42
|
-
return (react_1.default.createElement("input", { ...ariaAndDataAttrs, "aria-describedby": props["aria-describedby"], "aria-invalid": props.isInvalid, autoComplete: props.autoComplete, autoFocus: props.autoFocus, className: "uxf-input__element", disabled: props.isDisabled, form: props.form, id: props.id, inputMode: props.inputMode, max: props.max, maxLength: props.maxLength, min: props.min, minLength: props.minLength, name: props.name, onBlur: props.onBlur, onChange: onChange, onFocus: props.onFocus, onKeyDown: props.onKeyDown, pattern: props.pattern, placeholder: props.placeholder, readOnly: props.isReadOnly, ref: ref, step: props.step, style: props.style, tabIndex: props.isReadOnly ? -1 : undefined, type: props.type, value: props.value }));
|
|
42
|
+
return (react_1.default.createElement("input", { ...ariaAndDataAttrs, "aria-describedby": props["aria-describedby"], "aria-invalid": props.isInvalid, autoComplete: props.autoComplete, autoFocus: props.autoFocus, className: "uxf-input__element", disabled: props.isDisabled, form: props.form, id: props.id, inputMode: props.inputMode, max: props.max, maxLength: props.maxLength, min: props.min, minLength: props.minLength, name: props.name, onBeforeInput: props.onBeforeInput, onBlur: props.onBlur, onChange: onChange, onFocus: props.onFocus, onKeyDown: props.onKeyDown, onPaste: props.onPaste, pattern: props.pattern, placeholder: props.placeholder, readOnly: props.isReadOnly, ref: ref, step: props.step, style: props.style, tabIndex: props.isReadOnly ? -1 : undefined, type: props.type, value: props.value }));
|
|
43
43
|
});
|
|
44
44
|
exports.InputElement.displayName = "UxfUiInputElement";
|
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
package/modal/modal.stories.js
CHANGED
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.Default = Default;
|
|
37
37
|
exports.ModalProvider = ModalProvider;
|
|
38
|
+
exports.ModalProviderWithLayers = ModalProviderWithLayers;
|
|
38
39
|
const button_1 = require("@uxf/ui/button");
|
|
39
40
|
const dialog_1 = require("@uxf/ui/dialog");
|
|
40
41
|
const react_1 = __importStar(require("react"));
|
|
@@ -68,52 +69,96 @@ function ModalProvider() {
|
|
|
68
69
|
children: (react_1.default.createElement(dialog_1.DialogPanel, null,
|
|
69
70
|
react_1.default.createElement("div", { className: "p-4" },
|
|
70
71
|
react_1.default.createElement("p", { className: "mb-3 text-lg font-bold" }, "Modal - default size"),
|
|
71
|
-
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: modal_service_1.closeModal }, "Close modal")))),
|
|
72
|
+
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: () => (0, modal_service_1.closeModal)() }, "Close modal")))),
|
|
72
73
|
onClose: () => console.log("modal closed"),
|
|
73
74
|
}) }, "Click to open modal - default"),
|
|
74
75
|
react_1.default.createElement(button_1.Button, { onClick: () => (0, modal_service_1.openModal)({
|
|
75
76
|
children: (react_1.default.createElement(dialog_1.DialogPanel, { width: "xs" },
|
|
76
77
|
react_1.default.createElement("div", { className: "p-4" },
|
|
77
78
|
react_1.default.createElement("p", { className: "mb-3 text-lg font-bold" }, "Modal - xs size"),
|
|
78
|
-
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: modal_service_1.closeModal }, "Close modal")))),
|
|
79
|
+
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: () => (0, modal_service_1.closeModal)() }, "Close modal")))),
|
|
79
80
|
}) }, "Click to open modal - xs"),
|
|
80
81
|
react_1.default.createElement(button_1.Button, { onClick: () => (0, modal_service_1.openModal)({
|
|
81
82
|
children: (react_1.default.createElement(dialog_1.DialogPanel, { width: "sm" },
|
|
82
83
|
react_1.default.createElement("div", { className: "p-4" },
|
|
83
84
|
react_1.default.createElement("p", { className: "mb-3 text-lg font-bold" }, "Modal - sm size"),
|
|
84
|
-
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: modal_service_1.closeModal }, "Close modal")))),
|
|
85
|
+
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: () => (0, modal_service_1.closeModal)() }, "Close modal")))),
|
|
85
86
|
}) }, "Click to open modal - sm"),
|
|
86
87
|
react_1.default.createElement(button_1.Button, { onClick: () => (0, modal_service_1.openModal)({
|
|
87
88
|
children: (react_1.default.createElement(dialog_1.DialogPanel, { width: "lg" },
|
|
88
89
|
react_1.default.createElement("div", { className: "p-4" },
|
|
89
90
|
react_1.default.createElement("p", { className: "mb-3 text-lg font-bold" }, "Modal - lg size"),
|
|
90
|
-
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: modal_service_1.closeModal }, "Close modal")))),
|
|
91
|
+
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: () => (0, modal_service_1.closeModal)() }, "Close modal")))),
|
|
91
92
|
}) }, "Click to open modal - lg"),
|
|
92
93
|
react_1.default.createElement(button_1.Button, { onClick: () => (0, modal_service_1.openModal)({
|
|
93
94
|
children: (react_1.default.createElement(dialog_1.DialogPanel, { width: "xl" },
|
|
94
95
|
react_1.default.createElement("div", { className: "p-4" },
|
|
95
96
|
react_1.default.createElement("p", { className: "mb-3 text-lg font-bold" }, "Modal - xl size"),
|
|
96
|
-
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: modal_service_1.closeModal }, "Close modal")))),
|
|
97
|
+
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: () => (0, modal_service_1.closeModal)() }, "Close modal")))),
|
|
97
98
|
}) }, "Click to open modal - xl"),
|
|
98
99
|
react_1.default.createElement(button_1.Button, { onClick: () => (0, modal_service_1.openModal)({
|
|
99
100
|
children: (react_1.default.createElement(dialog_1.DialogPanel, null,
|
|
100
101
|
react_1.default.createElement("div", { className: "p-4" },
|
|
101
102
|
react_1.default.createElement("p", { className: "mb-3 text-lg font-bold" }, "Modal - dark"),
|
|
102
|
-
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: modal_service_1.closeModal }, "Close modal")))),
|
|
103
|
+
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: () => (0, modal_service_1.closeModal)() }, "Close modal")))),
|
|
103
104
|
className: "dark",
|
|
104
105
|
}) }, "Click to open modal - dark"),
|
|
105
106
|
react_1.default.createElement(button_1.Button, { onClick: () => (0, modal_service_1.openModal)({
|
|
106
107
|
children: (react_1.default.createElement(dialog_1.DialogPanel, null,
|
|
107
108
|
react_1.default.createElement("div", { className: "p-4" },
|
|
108
109
|
react_1.default.createElement("p", { className: "mb-3 text-lg font-bold" }, "Modal - disable backdrop close"),
|
|
109
|
-
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: modal_service_1.closeModal }, "Close modal")))),
|
|
110
|
+
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: () => (0, modal_service_1.closeModal)() }, "Close modal")))),
|
|
110
111
|
isBackdropCloseDisabled: true,
|
|
111
112
|
}) }, "Click to open modal - disable backdrop close"),
|
|
112
113
|
react_1.default.createElement(button_1.Button, { onClick: () => (0, modal_service_1.openModal)({
|
|
113
114
|
children: (react_1.default.createElement(dialog_1.DialogPanel, null,
|
|
114
115
|
react_1.default.createElement("div", { className: "p-4" },
|
|
115
116
|
react_1.default.createElement("p", { className: "mb-3 text-lg font-bold" }, "Modal - variant right drawer"),
|
|
116
|
-
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: modal_service_1.closeModal }, "Close modal")))),
|
|
117
|
+
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: () => (0, modal_service_1.closeModal)() }, "Close modal")))),
|
|
117
118
|
variant: "drawer-right",
|
|
118
119
|
}) }, "Click to open modal - variant right drawer"))));
|
|
119
120
|
}
|
|
121
|
+
function ModalProviderWithLayers() {
|
|
122
|
+
return (react_1.default.createElement("div", { className: "space-y-2 p-20 dark:text-white" },
|
|
123
|
+
react_1.default.createElement("div", { className: "space-y-4" },
|
|
124
|
+
react_1.default.createElement("h3", { className: "uxf-typo-h5 mb-2" }, "Layer Management Demo"),
|
|
125
|
+
react_1.default.createElement("div", { className: "space-x-4" },
|
|
126
|
+
react_1.default.createElement(button_1.Button, { onClick: () => (0, modal_service_1.openModal)({
|
|
127
|
+
children: (react_1.default.createElement(dialog_1.DialogPanel, { width: "xl" },
|
|
128
|
+
react_1.default.createElement("div", { className: "p-4" },
|
|
129
|
+
react_1.default.createElement("p", { className: "mb-2 text-lg font-bold" }, "Main Layer Modal"),
|
|
130
|
+
react_1.default.createElement("p", { className: "mb-4 text-sm text-gray-600" }, "This is in the \"main\" layer (default)"),
|
|
131
|
+
react_1.default.createElement("div", { className: "space-y-2" },
|
|
132
|
+
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: () => (0, modal_service_1.closeModal)("main") }, "Close Main Layer"),
|
|
133
|
+
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: () => (0, modal_service_1.openModal)({
|
|
134
|
+
children: (react_1.default.createElement(dialog_1.DialogPanel, { width: "sm" },
|
|
135
|
+
react_1.default.createElement("div", { className: "p-4" },
|
|
136
|
+
react_1.default.createElement("p", { className: "mb-2 text-lg font-bold" }, "Confirmation Layer"),
|
|
137
|
+
react_1.default.createElement("p", { className: "mb-4 text-sm text-gray-600" }, "This is in the \"confirmation\" layer (above main)"),
|
|
138
|
+
react_1.default.createElement(button_1.Button, { isFullWidth: true, onClick: () => (0, modal_service_1.closeModal)("confirm") }, "Close Confirmation")))),
|
|
139
|
+
}, { layer: "confirm" }) }, "Open Confirmation Layer"))))),
|
|
140
|
+
}, { layer: "main" }) }, "Open in Main Layer"),
|
|
141
|
+
react_1.default.createElement(button_1.Button, { onClick: () => {
|
|
142
|
+
// Open multiple modals in different layers
|
|
143
|
+
(0, modal_service_1.openModal)({
|
|
144
|
+
children: (react_1.default.createElement(dialog_1.DialogPanel, { width: "xl" },
|
|
145
|
+
react_1.default.createElement("div", { className: "p-4" },
|
|
146
|
+
react_1.default.createElement("p", { className: "mb-2 text-lg font-bold" }, "Main Layer"),
|
|
147
|
+
react_1.default.createElement("p", { className: "text-sm text-gray-600" }, "Layer 0 - z-index: 100")))),
|
|
148
|
+
}, { layer: "main" });
|
|
149
|
+
(0, modal_service_1.openModal)({
|
|
150
|
+
children: (react_1.default.createElement(dialog_1.DialogPanel, { width: "default" },
|
|
151
|
+
react_1.default.createElement("div", { className: "p-4" },
|
|
152
|
+
react_1.default.createElement("p", { className: "mb-2 text-lg font-bold" }, "Confirmation Layer"),
|
|
153
|
+
react_1.default.createElement("p", { className: "text-sm text-gray-600" }, "Layer 1 - z-index: 101")))),
|
|
154
|
+
}, { layer: "confirm" });
|
|
155
|
+
}, variant: "secondary" }, "Open All 2 Layers")),
|
|
156
|
+
react_1.default.createElement("h3", { className: "uxf-typo-h5 mb-2" }, "Default Layer Configuration"),
|
|
157
|
+
react_1.default.createElement("ul", { className: "space-y-1" },
|
|
158
|
+
react_1.default.createElement("li", null,
|
|
159
|
+
react_1.default.createElement("strong", null, "main:"),
|
|
160
|
+
" z-index 100"),
|
|
161
|
+
react_1.default.createElement("li", null,
|
|
162
|
+
react_1.default.createElement("strong", null, "confirm:"),
|
|
163
|
+
" z-index 101")))));
|
|
164
|
+
}
|
package/modal/theme.d.ts
CHANGED
package/modal/types.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Noop } from "@uxf/core/types";
|
|
2
|
-
import { ModalVariants } from "@uxf/ui/modal/theme";
|
|
3
|
-
import { ReactNode } from "react";
|
|
2
|
+
import { type ModalLayers, ModalVariants } from "@uxf/ui/modal/theme";
|
|
3
|
+
import { CSSProperties, ReactNode } from "react";
|
|
4
4
|
import type { DialogProps } from "../dialog";
|
|
5
5
|
export type ModalVariant = keyof ModalVariants;
|
|
6
|
+
export type ModalLayerName = keyof ModalLayers;
|
|
6
7
|
export interface ModalProviderProps {
|
|
7
8
|
children: ReactNode;
|
|
8
9
|
className?: DialogProps["className"];
|
|
@@ -22,5 +23,40 @@ export interface ModalProps {
|
|
|
22
23
|
isEscKeyCloseDisabled?: boolean;
|
|
23
24
|
isOpen: boolean;
|
|
24
25
|
onClose: Noop;
|
|
26
|
+
style?: CSSProperties;
|
|
25
27
|
variant?: ModalVariant;
|
|
26
28
|
}
|
|
29
|
+
export interface ModalDialogRendererProps {
|
|
30
|
+
instance: ModalInstance;
|
|
31
|
+
isTopmost: boolean;
|
|
32
|
+
onClose: Noop;
|
|
33
|
+
}
|
|
34
|
+
export interface ModalLayerConfig {
|
|
35
|
+
name: ModalLayerName;
|
|
36
|
+
zIndex: number;
|
|
37
|
+
}
|
|
38
|
+
export interface ModalLayersConfiguration {
|
|
39
|
+
layers: Record<ModalLayerName, ModalLayerConfig>;
|
|
40
|
+
defaultLayer: ModalLayerName;
|
|
41
|
+
}
|
|
42
|
+
export interface OpenModalOptions {
|
|
43
|
+
layer?: ModalLayerName;
|
|
44
|
+
shouldReplace?: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface ModalInstance {
|
|
47
|
+
id: string;
|
|
48
|
+
layer: ModalLayerName;
|
|
49
|
+
props: ModalProviderProps;
|
|
50
|
+
zIndex: number;
|
|
51
|
+
}
|
|
52
|
+
export interface ModalStackRef {
|
|
53
|
+
openInLayer: (modal: ModalProviderProps, options?: OpenModalOptions) => string;
|
|
54
|
+
closeLayer: (layer: string) => void;
|
|
55
|
+
closeTopmost: Noop;
|
|
56
|
+
closeAll: Noop;
|
|
57
|
+
}
|
|
58
|
+
export interface DialogProviderComponentProps {
|
|
59
|
+
className?: string;
|
|
60
|
+
variant?: ModalVariant;
|
|
61
|
+
style?: CSSProperties;
|
|
62
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uxf/ui",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.104.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"@floating-ui/react": "0.27.17",
|
|
25
25
|
"@headlessui/react": "1.7.19",
|
|
26
26
|
"@uxf/core": "11.100.0",
|
|
27
|
-
"@uxf/core-react": "11.
|
|
27
|
+
"@uxf/core-react": "11.104.0",
|
|
28
28
|
"@uxf/datepicker": "11.100.1",
|
|
29
29
|
"@uxf/localize": "11.92.1",
|
|
30
|
-
"@uxf/styles": "11.
|
|
30
|
+
"@uxf/styles": "11.104.0",
|
|
31
31
|
"color2k": "2.0.3",
|
|
32
32
|
"dayjs": "1.11.19",
|
|
33
33
|
"react-dropzone": "14.4.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InputProps } from "@uxf/ui/input/input";
|
|
2
2
|
import { FormControlProps } from "@uxf/ui/types";
|
|
3
|
-
import React, { CSSProperties, KeyboardEventHandler, ReactNode } from "react";
|
|
3
|
+
import React, { ClipboardEventHandler, CSSProperties, InputEventHandler, KeyboardEventHandler, ReactNode } from "react";
|
|
4
4
|
type EnterKeyHint = "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined;
|
|
5
5
|
type InputMode = "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | undefined;
|
|
6
6
|
export interface TextInputProps extends FormControlProps<string>, Pick<InputProps, "size" | "variant"> {
|
|
@@ -16,7 +16,9 @@ export interface TextInputProps extends FormControlProps<string>, Pick<InputProp
|
|
|
16
16
|
label?: ReactNode;
|
|
17
17
|
leftAddon?: ReactNode;
|
|
18
18
|
leftElement?: ReactNode;
|
|
19
|
+
onBeforeInput?: InputEventHandler<HTMLInputElement>;
|
|
19
20
|
onKeyDown?: KeyboardEventHandler<HTMLInputElement>;
|
|
21
|
+
onPaste?: ClipboardEventHandler<HTMLInputElement>;
|
|
20
22
|
placeholder?: string;
|
|
21
23
|
rightAddon?: ReactNode;
|
|
22
24
|
rightElement?: ReactNode;
|
package/text-input/text-input.js
CHANGED
|
@@ -55,7 +55,7 @@ exports.TextInput = (0, react_1.forwardRef)((props, ref) => {
|
|
|
55
55
|
react_1.default.createElement(input_1.Input, { inputFocus: input, ref: (0, compose_refs_1.composeRefs)(innerRef, ref), size: props.size, style: props.style, variant: props.variant },
|
|
56
56
|
props.leftAddon && react_1.default.createElement(input_1.Input.LeftAddon, null, props.leftAddon),
|
|
57
57
|
props.leftElement && react_1.default.createElement(input_1.Input.LeftElement, null, props.leftElement),
|
|
58
|
-
react_1.default.createElement(input_1.Input.Element, { "aria-describedby": errorId, autoComplete: props.autoComplete, autoFocus: props.autoFocus, form: props.form, id: id, inputMode: props.inputMode, isDisabled: props.isDisabled, isInvalid: props.isInvalid, isReadOnly: props.isReadOnly, isRequired: props.isRequired, max: props.max, maxLength: props.maxLength, min: props.min, minLength: props.minLength, name: props.name, onChange: props.onChange, onKeyDown: props.onKeyDown, pattern: props.pattern, placeholder: props.placeholder, step: props.step, type: props.type, value: props.value }),
|
|
58
|
+
react_1.default.createElement(input_1.Input.Element, { "aria-describedby": errorId, autoComplete: props.autoComplete, autoFocus: props.autoFocus, form: props.form, id: id, inputMode: props.inputMode, isDisabled: props.isDisabled, isInvalid: props.isInvalid, isReadOnly: props.isReadOnly, isRequired: props.isRequired, max: props.max, maxLength: props.maxLength, min: props.min, minLength: props.minLength, name: props.name, onBeforeInput: props.onBeforeInput, onChange: props.onChange, onKeyDown: props.onKeyDown, onPaste: props.onPaste, pattern: props.pattern, placeholder: props.placeholder, step: props.step, type: props.type, value: props.value }),
|
|
59
59
|
props.rightElement && react_1.default.createElement(input_1.Input.RightElement, null, props.rightElement),
|
|
60
60
|
props.rightAddon && react_1.default.createElement(input_1.Input.RightAddon, null, props.rightAddon))));
|
|
61
61
|
});
|
package/tw-tokens/tw-z-index.js
CHANGED