@4i/modal-manager 1.1.22 → 1.1.30
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/package.json +42 -42
- package/readme.md +285 -285
- package/src/components/modal-provider.d.ts +9 -6
- package/src/components/modal-provider.js +91 -85
- package/src/components/modal-provider.tsx +177 -165
- package/src/index.ts +5 -5
- package/src/styles.css +53 -77
- package/src/utils/Manager.ts +27 -27
- package/src/utils/ModalManager.d.ts +6 -2
- package/src/utils/ModalManager.js +44 -12
- package/src/utils/ModalManager.ts +131 -94
- package/tsconfig.json +17 -17
@@ -1,16 +1,19 @@
|
|
1
1
|
import React from "react";
|
2
2
|
export type ModalList = {
|
3
|
-
[key: string]: React.ComponentType
|
3
|
+
[key: string]: React.ComponentType<any>;
|
4
4
|
};
|
5
5
|
interface ModalProviderProps {
|
6
|
-
modalList:
|
6
|
+
modalList: ModalList;
|
7
7
|
isOverflow?: boolean;
|
8
8
|
className?: string;
|
9
9
|
backdropClassName?: string;
|
10
|
-
onModalStateChange?: (modalState: boolean, data:
|
10
|
+
onModalStateChange?: (modalState: boolean, data: ModalData[], names: string[]) => void;
|
11
11
|
}
|
12
|
-
type
|
13
|
-
|
12
|
+
type ModalData = {
|
13
|
+
id: string;
|
14
|
+
name: string;
|
15
|
+
payload: any;
|
16
|
+
options?: any;
|
14
17
|
};
|
15
|
-
declare const ModalProvider:
|
18
|
+
declare const ModalProvider: React.FC<ModalProviderProps>;
|
16
19
|
export default ModalProvider;
|
@@ -81,116 +81,122 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
81
81
|
};
|
82
82
|
Object.defineProperty(exports, "__esModule", { value: true });
|
83
83
|
var react_1 = __importStar(require("react"));
|
84
|
-
var ModalManager_1 =
|
84
|
+
var ModalManager_1 = require("../utils/ModalManager");
|
85
85
|
var ModalProvider = function (_a) {
|
86
|
-
var modalList = _a.modalList,
|
87
|
-
var
|
88
|
-
var
|
89
|
-
var
|
90
|
-
var applyCloseStyles = function (index) {
|
86
|
+
var modalList = _a.modalList, _b = _a.isOverflow, isOverflow = _b === void 0 ? true : _b, _c = _a.className, className = _c === void 0 ? "" : _c, _d = _a.backdropClassName, backdropClassName = _d === void 0 ? "" : _d, onModalStateChange = _a.onModalStateChange;
|
87
|
+
var _e = (0, react_1.useState)([]), modals = _e[0], setModals = _e[1];
|
88
|
+
var modalRefs = (0, react_1.useRef)(new Map());
|
89
|
+
var applyCloseStyles = function (modalIndex) {
|
91
90
|
return new Promise(function (resolve) {
|
92
|
-
var
|
93
|
-
if (!
|
91
|
+
var modalElement = document.querySelector("[data-modal-index=\"".concat(modalIndex, "\"]"));
|
92
|
+
if (!modalElement) {
|
93
|
+
resolve(false);
|
94
94
|
return;
|
95
|
-
|
95
|
+
}
|
96
|
+
modalElement.classList.add("modal_closing");
|
96
97
|
setTimeout(function () {
|
97
98
|
resolve(true);
|
98
|
-
},
|
99
|
+
}, 300);
|
99
100
|
});
|
100
101
|
};
|
101
102
|
(0, react_1.useEffect)(function () {
|
102
|
-
if (
|
103
|
-
return;
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
}, [
|
103
|
+
if (onModalStateChange && modals.length >= 0) {
|
104
|
+
var data = modals.map(function (modal) { return modal.payload; });
|
105
|
+
var names = modals.map(function (modal) { return modal.name; });
|
106
|
+
onModalStateChange(modals.length > 0, data, names);
|
107
|
+
}
|
108
|
+
}, [modals, onModalStateChange]);
|
108
109
|
(0, react_1.useEffect)(function () {
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
110
|
+
// Toggle body overflow
|
111
|
+
if (typeof document !== "undefined") {
|
112
|
+
document.body.style.overflow =
|
113
|
+
isOverflow && modals.length > 0 ? "hidden" : "";
|
114
|
+
}
|
115
|
+
}, [modals.length, isOverflow]);
|
116
|
+
(0, react_1.useEffect)(function () {
|
117
|
+
var handleOpenModal = function (name, payload, options) {
|
118
|
+
var id = "modal-".concat(payload.modalId || Date.now(), "-").concat(Math.random()
|
119
|
+
.toString(36)
|
120
|
+
.substring(2, 9));
|
121
|
+
setModals(function (prevModals) { return __spreadArray(__spreadArray([], prevModals, true), [
|
122
|
+
{ id: id, name: name, payload: payload, options: options },
|
123
|
+
], false); });
|
117
124
|
};
|
118
125
|
var handleClose = function (position) { return __awaiter(void 0, void 0, void 0, function () {
|
126
|
+
var i, indexToRemove_1;
|
119
127
|
return __generator(this, function (_a) {
|
120
128
|
switch (_a.label) {
|
121
129
|
case 0:
|
122
|
-
console.log("
|
123
|
-
return [
|
130
|
+
console.log("POS", position);
|
131
|
+
if (!(position === "all")) return [3 /*break*/, 5];
|
132
|
+
i = modals.length - 1;
|
133
|
+
_a.label = 1;
|
124
134
|
case 1:
|
135
|
+
if (!(i >= 0)) return [3 /*break*/, 4];
|
136
|
+
return [4 /*yield*/, applyCloseStyles(i)];
|
137
|
+
case 2:
|
125
138
|
_a.sent();
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
if (
|
137
|
-
//
|
138
|
-
|
139
|
-
return prev.filter(function (_, index) { return index !== prev.length - 1; });
|
140
|
-
});
|
141
|
-
setNames(function (prev) {
|
142
|
-
return prev.filter(function (_, index) { return index !== prev.length - 1; });
|
143
|
-
});
|
144
|
-
return [2 /*return*/];
|
145
|
-
}
|
146
|
-
if (position === 0) {
|
147
|
-
// remove first
|
148
|
-
setData(function (prev) { return prev.filter(function (_, index) { return index !== 0; }); });
|
149
|
-
setNames(function (prev) { return prev.filter(function (_, index) { return index !== 0; }); });
|
150
|
-
return [2 /*return*/];
|
139
|
+
_a.label = 3;
|
140
|
+
case 3:
|
141
|
+
i--;
|
142
|
+
return [3 /*break*/, 1];
|
143
|
+
case 4:
|
144
|
+
setModals([]);
|
145
|
+
return [2 /*return*/];
|
146
|
+
case 5:
|
147
|
+
if (!(typeof position === "number")) return [3 /*break*/, 7];
|
148
|
+
indexToRemove_1 = position;
|
149
|
+
if (indexToRemove_1 < 0 || indexToRemove_1 >= modals.length) {
|
150
|
+
// Если индекс невалидный, закрываем последний
|
151
|
+
indexToRemove_1 = modals.length - 1;
|
151
152
|
}
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
return
|
153
|
+
if (!(indexToRemove_1 >= 0 && indexToRemove_1 < modals.length)) return [3 /*break*/, 7];
|
154
|
+
return [4 /*yield*/, applyCloseStyles(indexToRemove_1)];
|
155
|
+
case 6:
|
156
|
+
_a.sent();
|
157
|
+
setModals(function (prevModals) {
|
158
|
+
return prevModals.filter(function (_, index) { return index !== indexToRemove_1; });
|
158
159
|
});
|
159
|
-
|
160
|
+
_a.label = 7;
|
161
|
+
case 7: return [2 /*return*/];
|
160
162
|
}
|
161
163
|
});
|
162
164
|
}); };
|
163
|
-
|
164
|
-
ModalManager_1.
|
165
|
+
// Добавляем обработчики событий
|
166
|
+
ModalManager_1.modal.emitter.on(ModalManager_1.constants.CHANGE, handleOpenModal);
|
167
|
+
ModalManager_1.modal.emitter.on(ModalManager_1.constants.CLOSE, handleClose);
|
165
168
|
return function () {
|
166
|
-
|
167
|
-
ModalManager_1.
|
169
|
+
// Удаляем обработчики при размонтировании
|
170
|
+
ModalManager_1.modal.emitter.off(ModalManager_1.constants.CHANGE, handleOpenModal);
|
171
|
+
ModalManager_1.modal.emitter.off(ModalManager_1.constants.CLOSE, handleClose);
|
168
172
|
};
|
169
|
-
|
170
|
-
}, []);
|
171
|
-
var activeModals = names.map(function (name) {
|
172
|
-
var Component = modalList[name] || (function () { return react_1.default.createElement(react_1.default.Fragment, null); });
|
173
|
-
return Component;
|
174
|
-
});
|
173
|
+
}, [modals]);
|
175
174
|
var handleCloseModal = function (index) {
|
176
|
-
ModalManager_1.
|
175
|
+
ModalManager_1.modal.close(index);
|
176
|
+
};
|
177
|
+
var saveModalRef = function (id, ref) {
|
178
|
+
if (ref) {
|
179
|
+
modalRefs.current.set(id, ref);
|
180
|
+
}
|
181
|
+
else {
|
182
|
+
modalRefs.current.delete(id);
|
183
|
+
}
|
177
184
|
};
|
178
|
-
|
179
|
-
|
185
|
+
// Предотвратить всплытие клика для модального контента
|
186
|
+
var stopPropagation = function (e) {
|
187
|
+
e.stopPropagation();
|
180
188
|
};
|
181
|
-
return (react_1.default.createElement(react_1.default.Fragment, null,
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
react_1.default.createElement(Modal, __assign({}, item.data))))));
|
194
|
-
})));
|
189
|
+
return (react_1.default.createElement(react_1.default.Fragment, null, modals.map(function (modalItem, index) {
|
190
|
+
var name = modalItem.name, payload = modalItem.payload, options = modalItem.options, id = modalItem.id;
|
191
|
+
var Modal = modalList[name] || (function () { return react_1.default.createElement("div", null,
|
192
|
+
"Modal not found: ",
|
193
|
+
name); });
|
194
|
+
var hideBackdrop = (options === null || options === void 0 ? void 0 : options.hideBackdrop) || false;
|
195
|
+
var extraClass = (options === null || options === void 0 ? void 0 : options.extraClass) || "";
|
196
|
+
return (react_1.default.createElement("div", { key: id, "data-modal-id": id, "data-modal-index": index, className: "modal_container ".concat(extraClass) },
|
197
|
+
!hideBackdrop && (react_1.default.createElement("div", { onClick: function () { return handleCloseModal(index); }, className: "modal_backdrop" })),
|
198
|
+
react_1.default.createElement("div", { className: "".concat(className, " modal_paper"), onClick: stopPropagation, ref: function (ref) { return saveModalRef(id, ref); } },
|
199
|
+
react_1.default.createElement(Modal, __assign({}, (payload.data || {}), { modalIndex: index })))));
|
200
|
+
})));
|
195
201
|
};
|
196
202
|
exports.default = ModalProvider;
|
@@ -1,165 +1,177 @@
|
|
1
|
-
"use client";
|
2
|
-
|
3
|
-
import React, { useEffect, useRef, useState } from "react";
|
4
|
-
import modal,
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
const
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
//
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
1
|
+
"use client";
|
2
|
+
|
3
|
+
import React, { useEffect, useRef, useState } from "react";
|
4
|
+
import { modal, constants } from "../utils/ModalManager";
|
5
|
+
|
6
|
+
export type ModalList = { [key: string]: React.ComponentType<any> };
|
7
|
+
|
8
|
+
interface ModalProviderProps {
|
9
|
+
modalList: ModalList;
|
10
|
+
isOverflow?: boolean;
|
11
|
+
className?: string;
|
12
|
+
backdropClassName?: string;
|
13
|
+
onModalStateChange?: (
|
14
|
+
modalState: boolean,
|
15
|
+
data: ModalData[],
|
16
|
+
names: string[]
|
17
|
+
) => void;
|
18
|
+
}
|
19
|
+
|
20
|
+
type ModalData = {
|
21
|
+
id: string;
|
22
|
+
name: string;
|
23
|
+
payload: any;
|
24
|
+
options?: any;
|
25
|
+
};
|
26
|
+
|
27
|
+
const ModalProvider: React.FC<ModalProviderProps> = ({
|
28
|
+
modalList,
|
29
|
+
isOverflow = true,
|
30
|
+
className = "",
|
31
|
+
backdropClassName = "",
|
32
|
+
onModalStateChange,
|
33
|
+
}) => {
|
34
|
+
const [modals, setModals] = useState<ModalData[]>([]);
|
35
|
+
const modalRefs = useRef<Map<string, HTMLDivElement | null>>(new Map());
|
36
|
+
|
37
|
+
const applyCloseStyles = (modalIndex: number): Promise<boolean> => {
|
38
|
+
return new Promise((resolve) => {
|
39
|
+
const modalElement = document.querySelector(
|
40
|
+
`[data-modal-index="${modalIndex}"]`
|
41
|
+
) as HTMLElement;
|
42
|
+
|
43
|
+
if (!modalElement) {
|
44
|
+
resolve(false);
|
45
|
+
return;
|
46
|
+
}
|
47
|
+
|
48
|
+
modalElement.classList.add("modal_closing");
|
49
|
+
setTimeout(() => {
|
50
|
+
resolve(true);
|
51
|
+
}, 300);
|
52
|
+
});
|
53
|
+
};
|
54
|
+
|
55
|
+
useEffect(() => {
|
56
|
+
if (onModalStateChange && modals.length >= 0) {
|
57
|
+
const data = modals.map((modal) => modal.payload);
|
58
|
+
const names = modals.map((modal) => modal.name);
|
59
|
+
onModalStateChange(modals.length > 0, data, names);
|
60
|
+
}
|
61
|
+
}, [modals, onModalStateChange]);
|
62
|
+
|
63
|
+
useEffect(() => {
|
64
|
+
// Toggle body overflow
|
65
|
+
if (typeof document !== "undefined") {
|
66
|
+
document.body.style.overflow =
|
67
|
+
isOverflow && modals.length > 0 ? "hidden" : "";
|
68
|
+
}
|
69
|
+
}, [modals.length, isOverflow]);
|
70
|
+
|
71
|
+
useEffect(() => {
|
72
|
+
const handleOpenModal = (name: string, payload: any, options?: any) => {
|
73
|
+
const id = `modal-${payload.modalId || Date.now()}-${Math.random()
|
74
|
+
.toString(36)
|
75
|
+
.substring(2, 9)}`;
|
76
|
+
|
77
|
+
setModals((prevModals) => [
|
78
|
+
...prevModals,
|
79
|
+
{ id, name, payload, options },
|
80
|
+
]);
|
81
|
+
};
|
82
|
+
|
83
|
+
const handleClose = async (position: number | string) => {
|
84
|
+
console.log("POS", position);
|
85
|
+
if (position === "all") {
|
86
|
+
// Закрыть все модальные окна с анимацией
|
87
|
+
for (let i = modals.length - 1; i >= 0; i--) {
|
88
|
+
await applyCloseStyles(i);
|
89
|
+
}
|
90
|
+
setModals([]);
|
91
|
+
return;
|
92
|
+
}
|
93
|
+
|
94
|
+
if (typeof position === "number") {
|
95
|
+
// Обработка числовых позиций
|
96
|
+
let indexToRemove: number = position;
|
97
|
+
|
98
|
+
if (indexToRemove < 0 || indexToRemove >= modals.length) {
|
99
|
+
// Если индекс невалидный, закрываем последний
|
100
|
+
indexToRemove = modals.length - 1;
|
101
|
+
}
|
102
|
+
|
103
|
+
if (indexToRemove >= 0 && indexToRemove < modals.length) {
|
104
|
+
await applyCloseStyles(indexToRemove);
|
105
|
+
|
106
|
+
setModals((prevModals) =>
|
107
|
+
prevModals.filter((_, index) => index !== indexToRemove)
|
108
|
+
);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
};
|
112
|
+
|
113
|
+
// Добавляем обработчики событий
|
114
|
+
modal.emitter.on(constants.CHANGE, handleOpenModal);
|
115
|
+
modal.emitter.on(constants.CLOSE, handleClose);
|
116
|
+
|
117
|
+
return () => {
|
118
|
+
// Удаляем обработчики при размонтировании
|
119
|
+
modal.emitter.off(constants.CHANGE, handleOpenModal);
|
120
|
+
modal.emitter.off(constants.CLOSE, handleClose);
|
121
|
+
};
|
122
|
+
}, [modals]);
|
123
|
+
|
124
|
+
const handleCloseModal = (index: number) => {
|
125
|
+
modal.close(index);
|
126
|
+
};
|
127
|
+
|
128
|
+
const saveModalRef = (id: string, ref: HTMLDivElement | null) => {
|
129
|
+
if (ref) {
|
130
|
+
modalRefs.current.set(id, ref);
|
131
|
+
} else {
|
132
|
+
modalRefs.current.delete(id);
|
133
|
+
}
|
134
|
+
};
|
135
|
+
|
136
|
+
// Предотвратить всплытие клика для модального контента
|
137
|
+
const stopPropagation = (e: React.MouseEvent) => {
|
138
|
+
e.stopPropagation();
|
139
|
+
};
|
140
|
+
|
141
|
+
return (
|
142
|
+
<>
|
143
|
+
{modals.map((modalItem, index) => {
|
144
|
+
const { name, payload, options, id } = modalItem;
|
145
|
+
const Modal =
|
146
|
+
modalList[name] || (() => <div>Modal not found: {name}</div>);
|
147
|
+
const hideBackdrop = options?.hideBackdrop || false;
|
148
|
+
const extraClass = options?.extraClass || "";
|
149
|
+
|
150
|
+
return (
|
151
|
+
<div
|
152
|
+
key={id}
|
153
|
+
data-modal-id={id}
|
154
|
+
data-modal-index={index}
|
155
|
+
className={`modal_container ${extraClass}`}
|
156
|
+
>
|
157
|
+
{!hideBackdrop && (
|
158
|
+
<div
|
159
|
+
onClick={() => handleCloseModal(index)}
|
160
|
+
className="modal_backdrop"
|
161
|
+
/>
|
162
|
+
)}
|
163
|
+
<div
|
164
|
+
className={`${className} modal_paper`}
|
165
|
+
onClick={stopPropagation}
|
166
|
+
ref={(ref) => saveModalRef(id, ref)}
|
167
|
+
>
|
168
|
+
<Modal {...(payload.data || {})} modalIndex={index} />
|
169
|
+
</div>
|
170
|
+
</div>
|
171
|
+
);
|
172
|
+
})}
|
173
|
+
</>
|
174
|
+
);
|
175
|
+
};
|
176
|
+
|
177
|
+
export default ModalProvider;
|
package/src/index.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import Manager from "./utils/Manager";
|
2
|
-
import modal from "./utils/ModalManager";
|
3
|
-
import ModalProvider from "./components/modal-provider";
|
4
|
-
|
5
|
-
export { Manager, modal, ModalProvider };
|
1
|
+
import Manager from "./utils/Manager";
|
2
|
+
import modal from "./utils/ModalManager";
|
3
|
+
import ModalProvider from "./components/modal-provider";
|
4
|
+
|
5
|
+
export { Manager, modal, ModalProvider };
|