@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.
@@ -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: any;
6
+ modalList: ModalList;
7
7
  isOverflow?: boolean;
8
8
  className?: string;
9
9
  backdropClassName?: string;
10
- onModalStateChange?: (modalState: boolean, data: TData[], names: string[]) => void;
10
+ onModalStateChange?: (modalState: boolean, data: ModalData[], names: string[]) => void;
11
11
  }
12
- type TData = {
13
- [key: string]: any;
12
+ type ModalData = {
13
+ id: string;
14
+ name: string;
15
+ payload: any;
16
+ options?: any;
14
17
  };
15
- declare const ModalProvider: ({ modalList, isOverflow, className, backdropClassName, onModalStateChange, }: ModalProviderProps) => React.JSX.Element;
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 = __importStar(require("../utils/ModalManager"));
84
+ var ModalManager_1 = require("../utils/ModalManager");
85
85
  var ModalProvider = function (_a) {
86
- var modalList = _a.modalList, isOverflow = _a.isOverflow, className = _a.className, backdropClassName = _a.backdropClassName, onModalStateChange = _a.onModalStateChange;
87
- var _b = (0, react_1.useState)([]), data = _b[0], setData = _b[1];
88
- var _c = (0, react_1.useState)([]), names = _c[0], setNames = _c[1];
89
- var modalRef = (0, react_1.useRef)([]);
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 modal = document.querySelector("[data-index=\"".concat(index, "\"]"));
93
- if (!modal)
91
+ var modalElement = document.querySelector("[data-modal-index=\"".concat(modalIndex, "\"]"));
92
+ if (!modalElement) {
93
+ resolve(false);
94
94
  return;
95
- modal.classList.add("closing");
95
+ }
96
+ modalElement.classList.add("modal_closing");
96
97
  setTimeout(function () {
97
98
  resolve(true);
98
- }, 150);
99
+ }, 300);
99
100
  });
100
101
  };
101
102
  (0, react_1.useEffect)(function () {
102
- if (!onModalStateChange)
103
- return;
104
- var modalState = data.length !== 0;
105
- onModalStateChange(modalState, data, names);
106
- // eslint-disable-next-line react-hooks/exhaustive-deps
107
- }, [data, names]);
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
- var handleOpenModal = function (name, data) {
110
- setData(function (prev) { return __spreadArray(__spreadArray([], prev, true), [data], false); });
111
- setNames(function (prev) { return __spreadArray(__spreadArray([], prev, true), [name], false); });
112
- if (isOverflow) {
113
- if (typeof document === "undefined")
114
- return;
115
- document.body.style.overflow = "hidden";
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("position", position);
123
- return [4 /*yield*/, applyCloseStyles(position)];
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
- if (isOverflow) {
127
- if (typeof document !== "undefined") {
128
- document.body.style.overflow = "";
129
- }
130
- }
131
- if (position === "all") {
132
- setData([]);
133
- setNames([]);
134
- return [2 /*return*/];
135
- }
136
- if (position === -1) {
137
- // remove last
138
- setData(function (prev) {
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
- // remove position index
153
- setData(function (prev) {
154
- return prev.filter(function (_, index) { return index !== prev.length - 1; });
155
- });
156
- setNames(function (prev) {
157
- return prev.filter(function (_, index) { return index !== prev.length - 1; });
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
- return [2 /*return*/];
160
+ _a.label = 7;
161
+ case 7: return [2 /*return*/];
160
162
  }
161
163
  });
162
164
  }); };
163
- ModalManager_1.default.addEventListener(ModalManager_1.constants.CHANGE, handleOpenModal);
164
- ModalManager_1.default.addEventListener(ModalManager_1.constants.CLOSE, handleClose);
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
- ModalManager_1.default.removeEventListener(ModalManager_1.constants.CHANGE, handleOpenModal);
167
- ModalManager_1.default.removeEventListener(ModalManager_1.constants.CLOSE, handleClose);
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
- // eslint-disable-next-line react-hooks/exhaustive-deps
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.default.close(index);
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
- var refReducer = function (index, value) {
179
- modalRef.current[index] = value;
185
+ // Предотвратить всплытие клика для модального контента
186
+ var stopPropagation = function (e) {
187
+ e.stopPropagation();
180
188
  };
181
- return (react_1.default.createElement(react_1.default.Fragment, null, data.length !== 0 &&
182
- data.map(function (item, i) {
183
- var Modal = activeModals[i] || (function () { return react_1.default.createElement(react_1.default.Fragment, null); });
184
- return (react_1.default.createElement("div", { "data-index": i, key: item.modalId, className: "modal-manager backdrop_modal_manager ".concat(backdropClassName) },
185
- react_1.default.createElement("div", { onClick: function (e) {
186
- e.stopPropagation();
187
- handleCloseModal(i);
188
- }, className: "backdrop" }),
189
- react_1.default.createElement("div", { className: "".concat(className, " modal_paper") },
190
- react_1.default.createElement("div", { ref: function (ref) {
191
- refReducer(i, ref);
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, { constants } from "../utils/ModalManager";
5
- import { createPortal } from "react-dom";
6
-
7
- export type ModalList = { [key: string]: React.ComponentType };
8
-
9
- interface ModalProviderProps {
10
- modalList: any;
11
- isOverflow?: boolean;
12
- className?: string;
13
- backdropClassName?: string;
14
- onModalStateChange?: (
15
- modalState: boolean,
16
- data: TData[],
17
- names: string[]
18
- ) => void;
19
- }
20
-
21
- type TData = { [key: string]: any };
22
-
23
- const ModalProvider = ({
24
- modalList,
25
- isOverflow,
26
- className,
27
- backdropClassName,
28
- onModalStateChange,
29
- }: ModalProviderProps) => {
30
- const [data, setData] = useState<TData[]>([]);
31
- const [names, setNames] = useState<string[]>([]);
32
- const modalRef = useRef<any[]>([]);
33
-
34
- const applyCloseStyles = (index: number) => {
35
- return new Promise((resolve) => {
36
- const modal = document.querySelector(
37
- `[data-index="${index}"]`
38
- ) as HTMLElement;
39
- if (!modal) return;
40
- modal.classList.add("closing");
41
- setTimeout(() => {
42
- resolve(true);
43
- }, 150);
44
- });
45
- };
46
-
47
- useEffect(() => {
48
- if (!onModalStateChange) return;
49
- const modalState = data.length !== 0;
50
- onModalStateChange(modalState, data, names);
51
- // eslint-disable-next-line react-hooks/exhaustive-deps
52
- }, [data, names]);
53
-
54
- useEffect(() => {
55
- const handleOpenModal = (name: string, data: TData) => {
56
- setData((prev: TData[]) => [...prev, data]);
57
- setNames((prev: string[]) => [...prev, name]);
58
-
59
- if (isOverflow) {
60
- if (typeof document === "undefined") return;
61
- document.body.style.overflow = "hidden";
62
- }
63
- };
64
-
65
- const handleClose = async (position: number | string) => {
66
- console.log("position", position);
67
- await applyCloseStyles(position as number);
68
- if (isOverflow) {
69
- if (typeof document !== "undefined") {
70
- document.body.style.overflow = "";
71
- }
72
- }
73
-
74
- if (position === "all") {
75
- setData([]);
76
- setNames([]);
77
- return;
78
- }
79
-
80
- if (position === -1) {
81
- // remove last
82
- setData((prev: TData[]) =>
83
- prev.filter((_, index) => index !== prev.length - 1)
84
- );
85
- setNames((prev: string[]) =>
86
- prev.filter((_, index) => index !== prev.length - 1)
87
- );
88
- return;
89
- }
90
-
91
- if (position === 0) {
92
- // remove first
93
- setData((prev: TData[]) => prev.filter((_, index) => index !== 0));
94
- setNames((prev: string[]) => prev.filter((_, index) => index !== 0));
95
- return;
96
- }
97
-
98
- // remove position index
99
- setData((prev: TData[]) =>
100
- prev.filter((_, index) => index !== prev.length - 1)
101
- );
102
- setNames((prev: string[]) =>
103
- prev.filter((_, index) => index !== prev.length - 1)
104
- );
105
- };
106
-
107
- modal.addEventListener(constants.CHANGE, handleOpenModal);
108
- modal.addEventListener(constants.CLOSE, handleClose);
109
- return () => {
110
- modal.removeEventListener(constants.CHANGE, handleOpenModal);
111
- modal.removeEventListener(constants.CLOSE, handleClose);
112
- };
113
- // eslint-disable-next-line react-hooks/exhaustive-deps
114
- }, []);
115
-
116
- const activeModals = names.map((name: string) => {
117
- const Component = modalList[name] || (() => <></>);
118
- return Component;
119
- });
120
-
121
- const handleCloseModal = (index: number) => {
122
- modal.close(index);
123
- };
124
-
125
- const refReducer = (index: number, value: any) => {
126
- modalRef.current[index] = value;
127
- };
128
-
129
- return (
130
- <>
131
- {data.length !== 0 &&
132
- data.map((item, i) => {
133
- const Modal = activeModals[i] || (() => <></>);
134
-
135
- return (
136
- <div
137
- data-index={i}
138
- key={item.modalId}
139
- className={`modal-manager backdrop_modal_manager ${backdropClassName}`}
140
- >
141
- <div
142
- onClick={(e) => {
143
- e.stopPropagation();
144
- handleCloseModal(i);
145
- }}
146
- className="backdrop"
147
- />
148
- {/* // h-full modal not close */}
149
- <div className={`${className} modal_paper`}>
150
- <div
151
- ref={(ref) => {
152
- refReducer(i, ref);
153
- }}
154
- >
155
- <Modal {...item.data} />
156
- </div>
157
- </div>
158
- </div>
159
- );
160
- })}
161
- </>
162
- );
163
- };
164
-
165
- export default ModalProvider;
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 };