@4i/modal-manager 1.1.21 → 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 -90
- package/src/components/modal-provider.tsx +177 -171
- 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,121 +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 =
|
85
|
-
var react_dom_1 = require("react-dom");
|
84
|
+
var ModalManager_1 = require("../utils/ModalManager");
|
86
85
|
var ModalProvider = function (_a) {
|
87
|
-
var modalList = _a.modalList,
|
88
|
-
var
|
89
|
-
var
|
90
|
-
var
|
91
|
-
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) {
|
92
90
|
return new Promise(function (resolve) {
|
93
|
-
var
|
94
|
-
if (!
|
91
|
+
var modalElement = document.querySelector("[data-modal-index=\"".concat(modalIndex, "\"]"));
|
92
|
+
if (!modalElement) {
|
93
|
+
resolve(false);
|
95
94
|
return;
|
96
|
-
|
95
|
+
}
|
96
|
+
modalElement.classList.add("modal_closing");
|
97
97
|
setTimeout(function () {
|
98
98
|
resolve(true);
|
99
|
-
},
|
99
|
+
}, 300);
|
100
100
|
});
|
101
101
|
};
|
102
102
|
(0, react_1.useEffect)(function () {
|
103
|
-
if (
|
104
|
-
return;
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
}, [
|
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]);
|
109
109
|
(0, react_1.useEffect)(function () {
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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); });
|
118
124
|
};
|
119
125
|
var handleClose = function (position) { return __awaiter(void 0, void 0, void 0, function () {
|
126
|
+
var i, indexToRemove_1;
|
120
127
|
return __generator(this, function (_a) {
|
121
128
|
switch (_a.label) {
|
122
129
|
case 0:
|
123
|
-
console.log("
|
124
|
-
return [
|
130
|
+
console.log("POS", position);
|
131
|
+
if (!(position === "all")) return [3 /*break*/, 5];
|
132
|
+
i = modals.length - 1;
|
133
|
+
_a.label = 1;
|
125
134
|
case 1:
|
135
|
+
if (!(i >= 0)) return [3 /*break*/, 4];
|
136
|
+
return [4 /*yield*/, applyCloseStyles(i)];
|
137
|
+
case 2:
|
126
138
|
_a.sent();
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
if (
|
138
|
-
//
|
139
|
-
|
140
|
-
return prev.filter(function (_, index) { return index !== prev.length - 1; });
|
141
|
-
});
|
142
|
-
setNames(function (prev) {
|
143
|
-
return prev.filter(function (_, index) { return index !== prev.length - 1; });
|
144
|
-
});
|
145
|
-
return [2 /*return*/];
|
146
|
-
}
|
147
|
-
if (position === 0) {
|
148
|
-
// remove first
|
149
|
-
setData(function (prev) { return prev.filter(function (_, index) { return index !== 0; }); });
|
150
|
-
setNames(function (prev) { return prev.filter(function (_, index) { return index !== 0; }); });
|
151
|
-
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;
|
152
152
|
}
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
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; });
|
159
159
|
});
|
160
|
-
|
160
|
+
_a.label = 7;
|
161
|
+
case 7: return [2 /*return*/];
|
161
162
|
}
|
162
163
|
});
|
163
164
|
}); };
|
164
|
-
|
165
|
-
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);
|
166
168
|
return function () {
|
167
|
-
|
168
|
-
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);
|
169
172
|
};
|
170
|
-
|
171
|
-
}, []);
|
172
|
-
var activeModals = names.map(function (name) {
|
173
|
-
var Component = modalList[name] || (function () { return react_1.default.createElement(react_1.default.Fragment, null); });
|
174
|
-
return Component;
|
175
|
-
});
|
173
|
+
}, [modals]);
|
176
174
|
var handleCloseModal = function (index) {
|
177
|
-
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
|
+
}
|
178
184
|
};
|
179
|
-
|
180
|
-
|
185
|
+
// Предотвратить всплытие клика для модального контента
|
186
|
+
var stopPropagation = function (e) {
|
187
|
+
e.stopPropagation();
|
181
188
|
};
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
react_1.default.createElement("div", { ref: function (ref) {
|
195
|
-
refReducer(i, ref);
|
196
|
-
} },
|
197
|
-
react_1.default.createElement(Modal, __assign({}, item.data))))));
|
198
|
-
})), body);
|
199
|
-
return null;
|
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
|
+
})));
|
200
201
|
};
|
201
202
|
exports.default = ModalProvider;
|
@@ -1,171 +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
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
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 };
|