@violice/rmu 0.2.5 → 0.2.7
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/README.md +4 -4
- package/dist/index.d.mts +49 -0
- package/dist/index.d.ts +49 -3
- package/dist/index.js +229 -6
- package/dist/index.mjs +204 -0
- package/package.json +37 -30
- package/src/emitter.ts +37 -0
- package/src/events.ts +3 -10
- package/src/index.ts +1 -1
- package/src/rmu-outlet.tsx +2 -2
- package/src/use-rmu-events.ts +8 -9
- package/src/use-rmu-state.ts +13 -13
- package/dist/events.d.ts +0 -13
- package/dist/rmu-context.d.ts +0 -3
- package/dist/rmu-outlet.d.ts +0 -5
- package/dist/rmu-provider.d.ts +0 -4
- package/dist/rmu.cjs.development.js +0 -269
- package/dist/rmu.cjs.development.js.map +0 -1
- package/dist/rmu.cjs.production.min.js +0 -2
- package/dist/rmu.cjs.production.min.js.map +0 -1
- package/dist/rmu.esm.js +0 -259
- package/dist/rmu.esm.js.map +0 -1
- package/dist/types.d.ts +0 -15
- package/dist/use-rmu-events.d.ts +0 -2
- package/dist/use-rmu-state.d.ts +0 -16
package/README.md
CHANGED
|
@@ -5,14 +5,14 @@ RMU is a small, zero-dependency utility to control modals in React apps
|
|
|
5
5
|
<!-- ## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install --save rmu
|
|
9
|
-
yarn add rmu
|
|
8
|
+
npm install --save @violice/rmu
|
|
9
|
+
yarn add @violice/rmu
|
|
10
10
|
``` -->
|
|
11
11
|
|
|
12
12
|
## Usage
|
|
13
13
|
|
|
14
14
|
```js
|
|
15
|
-
import { openModal, closeModal, RMUOutlet, RMUProvider } from 'rmu';
|
|
15
|
+
import { openModal, closeModal, RMUOutlet, RMUProvider } from '@violice/rmu';
|
|
16
16
|
|
|
17
17
|
const Example = () => {
|
|
18
18
|
return (
|
|
@@ -45,7 +45,7 @@ const Example = () => {
|
|
|
45
45
|
|
|
46
46
|
## RoadMap
|
|
47
47
|
- [x] Custom events, new API (0.2.0)
|
|
48
|
-
- [ ] Connected modals
|
|
48
|
+
- [ ] Connected modals
|
|
49
49
|
- [ ] Auto-close
|
|
50
50
|
- [ ] Use for toasts example
|
|
51
51
|
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/rmu-provider.d.ts
|
|
4
|
+
declare const RMUProvider: ({
|
|
5
|
+
children
|
|
6
|
+
}: {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
}) => React.JSX.Element;
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/rmu-outlet.d.ts
|
|
11
|
+
declare const RMUOutlet: ({
|
|
12
|
+
outletId
|
|
13
|
+
}: {
|
|
14
|
+
outletId?: string | undefined;
|
|
15
|
+
}) => React.JSX.Element;
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/types.d.ts
|
|
18
|
+
type RMUContextState = {
|
|
19
|
+
outlets: Record<string, Record<string, ReactNode>>;
|
|
20
|
+
openModal: ({
|
|
21
|
+
modalId,
|
|
22
|
+
modalComponent,
|
|
23
|
+
outletId
|
|
24
|
+
}: {
|
|
25
|
+
modalId: string;
|
|
26
|
+
modalComponent: ReactNode;
|
|
27
|
+
outletId: string;
|
|
28
|
+
}) => void;
|
|
29
|
+
closeModal: ({
|
|
30
|
+
modalId,
|
|
31
|
+
outletId
|
|
32
|
+
}: {
|
|
33
|
+
modalId: string;
|
|
34
|
+
outletId: string;
|
|
35
|
+
}) => void;
|
|
36
|
+
addOutlet: (outletId: string) => void;
|
|
37
|
+
removeOutlet: (outletId: string) => void;
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/events.d.ts
|
|
41
|
+
declare const openModal: (modalComponent: ReactNode, config?: {
|
|
42
|
+
outletId?: string;
|
|
43
|
+
}) => {
|
|
44
|
+
modalId: string;
|
|
45
|
+
outletId: string;
|
|
46
|
+
};
|
|
47
|
+
declare const closeModal: RMUContextState['closeModal'];
|
|
48
|
+
//#endregion
|
|
49
|
+
export { RMUOutlet, RMUProvider, closeModal, openModal };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/rmu-provider.d.ts
|
|
4
|
+
declare const RMUProvider: ({
|
|
5
|
+
children
|
|
6
|
+
}: {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
}) => React.JSX.Element;
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/rmu-outlet.d.ts
|
|
11
|
+
declare const RMUOutlet: ({
|
|
12
|
+
outletId
|
|
13
|
+
}: {
|
|
14
|
+
outletId?: string | undefined;
|
|
15
|
+
}) => React.JSX.Element;
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/types.d.ts
|
|
18
|
+
type RMUContextState = {
|
|
19
|
+
outlets: Record<string, Record<string, ReactNode>>;
|
|
20
|
+
openModal: ({
|
|
21
|
+
modalId,
|
|
22
|
+
modalComponent,
|
|
23
|
+
outletId
|
|
24
|
+
}: {
|
|
25
|
+
modalId: string;
|
|
26
|
+
modalComponent: ReactNode;
|
|
27
|
+
outletId: string;
|
|
28
|
+
}) => void;
|
|
29
|
+
closeModal: ({
|
|
30
|
+
modalId,
|
|
31
|
+
outletId
|
|
32
|
+
}: {
|
|
33
|
+
modalId: string;
|
|
34
|
+
outletId: string;
|
|
35
|
+
}) => void;
|
|
36
|
+
addOutlet: (outletId: string) => void;
|
|
37
|
+
removeOutlet: (outletId: string) => void;
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/events.d.ts
|
|
41
|
+
declare const openModal: (modalComponent: ReactNode, config?: {
|
|
42
|
+
outletId?: string;
|
|
43
|
+
}) => {
|
|
44
|
+
modalId: string;
|
|
45
|
+
outletId: string;
|
|
46
|
+
};
|
|
47
|
+
declare const closeModal: RMUContextState['closeModal'];
|
|
48
|
+
//#endregion
|
|
49
|
+
export { RMUOutlet, RMUProvider, closeModal, openModal };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,231 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
1
22
|
|
|
2
|
-
|
|
23
|
+
//#endregion
|
|
24
|
+
let react = require("react");
|
|
25
|
+
react = __toESM(react);
|
|
3
26
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
27
|
+
//#region src/rmu-context.ts
|
|
28
|
+
const RMUContext = (0, react.createContext)(null);
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/emitter.ts
|
|
32
|
+
const listenersByType = {};
|
|
33
|
+
const emitter = {
|
|
34
|
+
subscribe(type, handler) {
|
|
35
|
+
if (!listenersByType[type]) listenersByType[type] = /* @__PURE__ */ new Set();
|
|
36
|
+
listenersByType[type].add(handler);
|
|
37
|
+
},
|
|
38
|
+
unsubscribe(type, handler) {
|
|
39
|
+
const listeners = listenersByType[type];
|
|
40
|
+
if (listeners) {
|
|
41
|
+
listeners.delete(handler);
|
|
42
|
+
if (listeners.size === 0) delete listenersByType[type];
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
emit(type, payload) {
|
|
46
|
+
const listeners = listenersByType[type];
|
|
47
|
+
if (!listeners) return;
|
|
48
|
+
[...listeners].forEach((handler) => {
|
|
49
|
+
try {
|
|
50
|
+
handler(payload);
|
|
51
|
+
} catch {}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/events.ts
|
|
58
|
+
const RMU_EVENTS = {
|
|
59
|
+
open: "rmu:open-modal",
|
|
60
|
+
close: "rmu:close-modal"
|
|
61
|
+
};
|
|
62
|
+
const openModal = (modalComponent, config = {}) => {
|
|
63
|
+
const modalId = `rmu-modal-${(/* @__PURE__ */ new Date()).getTime().toString()}`;
|
|
64
|
+
const { outletId = "rmu-default-outlet" } = config;
|
|
65
|
+
emitter.emit(RMU_EVENTS.open, {
|
|
66
|
+
modalId,
|
|
67
|
+
modalComponent,
|
|
68
|
+
outletId
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
modalId,
|
|
72
|
+
outletId
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
const closeModal = ({ modalId, outletId }) => {
|
|
76
|
+
emitter.emit(RMU_EVENTS.close, {
|
|
77
|
+
modalId,
|
|
78
|
+
outletId
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/use-rmu-events.ts
|
|
84
|
+
const useRMUEvents = (ctx) => {
|
|
85
|
+
const events = {
|
|
86
|
+
open: (payload) => ctx.openModal(payload),
|
|
87
|
+
close: (payload) => ctx.closeModal(payload)
|
|
88
|
+
};
|
|
89
|
+
(0, react.useEffect)(() => {
|
|
90
|
+
Object.keys(events).forEach((event) => {
|
|
91
|
+
emitter.subscribe(RMU_EVENTS[event], events[event]);
|
|
92
|
+
});
|
|
93
|
+
return () => {
|
|
94
|
+
Object.keys(events).forEach((event) => {
|
|
95
|
+
emitter.unsubscribe(RMU_EVENTS[event], events[event]);
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
}, []);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/use-rmu-state.ts
|
|
103
|
+
const ACTIONS = {
|
|
104
|
+
openModal: "rmu:open-modal",
|
|
105
|
+
closeModal: "rmu:close-modal",
|
|
106
|
+
addOutlet: "rmu:add-modal",
|
|
107
|
+
removeOutlet: "rmu:remove-outlet"
|
|
108
|
+
};
|
|
109
|
+
const reducer = (state, action) => {
|
|
110
|
+
switch (action.type) {
|
|
111
|
+
case ACTIONS.openModal: {
|
|
112
|
+
const { modalId, modalComponent, outletId } = action.payload;
|
|
113
|
+
const modalOutlet = state.outlets[outletId];
|
|
114
|
+
if (!modalOutlet) throw new Error(`Outlet with id ${outletId} not found`);
|
|
115
|
+
return {
|
|
116
|
+
...state,
|
|
117
|
+
outlets: {
|
|
118
|
+
...state.outlets,
|
|
119
|
+
[outletId]: {
|
|
120
|
+
...modalOutlet,
|
|
121
|
+
[modalId]: modalComponent
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
case ACTIONS.closeModal: {
|
|
127
|
+
const { modalId, outletId } = action.payload;
|
|
128
|
+
const modalOutlet = state.outlets[outletId];
|
|
129
|
+
if (!modalOutlet) throw new Error(`Outlet with id ${outletId} not found`);
|
|
130
|
+
const { [modalId]: _removed,...restModals } = modalOutlet;
|
|
131
|
+
return {
|
|
132
|
+
...state,
|
|
133
|
+
outlets: {
|
|
134
|
+
...state.outlets,
|
|
135
|
+
[outletId]: restModals
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
case ACTIONS.addOutlet: {
|
|
140
|
+
const { outletId } = action.payload;
|
|
141
|
+
if (!!state.outlets[outletId]) throw new Error(`Outlet with id ${outletId} already exists`);
|
|
142
|
+
return {
|
|
143
|
+
...state,
|
|
144
|
+
outlets: {
|
|
145
|
+
...state.outlets,
|
|
146
|
+
[outletId]: {}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
case ACTIONS.removeOutlet: {
|
|
151
|
+
const { outletId } = action.payload;
|
|
152
|
+
const { [outletId]: _removed,...restOutlets } = state.outlets;
|
|
153
|
+
return {
|
|
154
|
+
...state,
|
|
155
|
+
outlets: restOutlets
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
default: return state;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
const useRMUState = () => {
|
|
162
|
+
const [state, dispatch] = (0, react.useReducer)(reducer, { outlets: {} });
|
|
163
|
+
const openModal$1 = ({ modalId, modalComponent, outletId }) => {
|
|
164
|
+
dispatch({
|
|
165
|
+
type: ACTIONS.openModal,
|
|
166
|
+
payload: {
|
|
167
|
+
modalId,
|
|
168
|
+
modalComponent,
|
|
169
|
+
outletId
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
const closeModal$1 = ({ modalId, outletId }) => {
|
|
174
|
+
dispatch({
|
|
175
|
+
type: ACTIONS.closeModal,
|
|
176
|
+
payload: {
|
|
177
|
+
modalId,
|
|
178
|
+
outletId
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
const addOutlet = (outletId) => {
|
|
183
|
+
dispatch({
|
|
184
|
+
type: ACTIONS.addOutlet,
|
|
185
|
+
payload: { outletId }
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
const removeOutlet = (outletId) => {
|
|
189
|
+
dispatch({
|
|
190
|
+
type: ACTIONS.removeOutlet,
|
|
191
|
+
payload: { outletId }
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
return {
|
|
195
|
+
...state,
|
|
196
|
+
openModal: openModal$1,
|
|
197
|
+
closeModal: closeModal$1,
|
|
198
|
+
addOutlet,
|
|
199
|
+
removeOutlet
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region src/rmu-provider.tsx
|
|
205
|
+
const RMUProvider = ({ children }) => {
|
|
206
|
+
const state = useRMUState();
|
|
207
|
+
useRMUEvents(state);
|
|
208
|
+
return /* @__PURE__ */ react.default.createElement(RMUContext.Provider, { value: state }, children);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/rmu-outlet.tsx
|
|
213
|
+
const RMUOutlet = ({ outletId = "rmu-default-outlet" }) => {
|
|
214
|
+
const ctx = (0, react.useContext)(RMUContext);
|
|
215
|
+
if (!ctx) throw new Error("RMUProvider not found in component tree");
|
|
216
|
+
const { outlets, addOutlet, removeOutlet } = ctx;
|
|
217
|
+
(0, react.useEffect)(() => {
|
|
218
|
+
addOutlet(outletId);
|
|
219
|
+
return () => {
|
|
220
|
+
removeOutlet(outletId);
|
|
221
|
+
};
|
|
222
|
+
}, []);
|
|
223
|
+
const modals = outlets[outletId] ?? {};
|
|
224
|
+
return /* @__PURE__ */ react.default.createElement(react.default.Fragment, null, Object.entries(modals).map(([modalId, modalComponent]) => /* @__PURE__ */ react.default.createElement(react.Fragment, { key: modalId }, modalComponent)));
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
//#endregion
|
|
228
|
+
exports.RMUOutlet = RMUOutlet;
|
|
229
|
+
exports.RMUProvider = RMUProvider;
|
|
230
|
+
exports.closeModal = closeModal;
|
|
231
|
+
exports.openModal = openModal;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import React, { Fragment, createContext, useContext, useEffect, useReducer } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/rmu-context.ts
|
|
4
|
+
const RMUContext = createContext(null);
|
|
5
|
+
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/emitter.ts
|
|
8
|
+
const listenersByType = {};
|
|
9
|
+
const emitter = {
|
|
10
|
+
subscribe(type, handler) {
|
|
11
|
+
if (!listenersByType[type]) listenersByType[type] = /* @__PURE__ */ new Set();
|
|
12
|
+
listenersByType[type].add(handler);
|
|
13
|
+
},
|
|
14
|
+
unsubscribe(type, handler) {
|
|
15
|
+
const listeners = listenersByType[type];
|
|
16
|
+
if (listeners) {
|
|
17
|
+
listeners.delete(handler);
|
|
18
|
+
if (listeners.size === 0) delete listenersByType[type];
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
emit(type, payload) {
|
|
22
|
+
const listeners = listenersByType[type];
|
|
23
|
+
if (!listeners) return;
|
|
24
|
+
[...listeners].forEach((handler) => {
|
|
25
|
+
try {
|
|
26
|
+
handler(payload);
|
|
27
|
+
} catch {}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/events.ts
|
|
34
|
+
const RMU_EVENTS = {
|
|
35
|
+
open: "rmu:open-modal",
|
|
36
|
+
close: "rmu:close-modal"
|
|
37
|
+
};
|
|
38
|
+
const openModal = (modalComponent, config = {}) => {
|
|
39
|
+
const modalId = `rmu-modal-${(/* @__PURE__ */ new Date()).getTime().toString()}`;
|
|
40
|
+
const { outletId = "rmu-default-outlet" } = config;
|
|
41
|
+
emitter.emit(RMU_EVENTS.open, {
|
|
42
|
+
modalId,
|
|
43
|
+
modalComponent,
|
|
44
|
+
outletId
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
modalId,
|
|
48
|
+
outletId
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
const closeModal = ({ modalId, outletId }) => {
|
|
52
|
+
emitter.emit(RMU_EVENTS.close, {
|
|
53
|
+
modalId,
|
|
54
|
+
outletId
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/use-rmu-events.ts
|
|
60
|
+
const useRMUEvents = (ctx) => {
|
|
61
|
+
const events = {
|
|
62
|
+
open: (payload) => ctx.openModal(payload),
|
|
63
|
+
close: (payload) => ctx.closeModal(payload)
|
|
64
|
+
};
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
Object.keys(events).forEach((event) => {
|
|
67
|
+
emitter.subscribe(RMU_EVENTS[event], events[event]);
|
|
68
|
+
});
|
|
69
|
+
return () => {
|
|
70
|
+
Object.keys(events).forEach((event) => {
|
|
71
|
+
emitter.unsubscribe(RMU_EVENTS[event], events[event]);
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
}, []);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/use-rmu-state.ts
|
|
79
|
+
const ACTIONS = {
|
|
80
|
+
openModal: "rmu:open-modal",
|
|
81
|
+
closeModal: "rmu:close-modal",
|
|
82
|
+
addOutlet: "rmu:add-modal",
|
|
83
|
+
removeOutlet: "rmu:remove-outlet"
|
|
84
|
+
};
|
|
85
|
+
const reducer = (state, action) => {
|
|
86
|
+
switch (action.type) {
|
|
87
|
+
case ACTIONS.openModal: {
|
|
88
|
+
const { modalId, modalComponent, outletId } = action.payload;
|
|
89
|
+
const modalOutlet = state.outlets[outletId];
|
|
90
|
+
if (!modalOutlet) throw new Error(`Outlet with id ${outletId} not found`);
|
|
91
|
+
return {
|
|
92
|
+
...state,
|
|
93
|
+
outlets: {
|
|
94
|
+
...state.outlets,
|
|
95
|
+
[outletId]: {
|
|
96
|
+
...modalOutlet,
|
|
97
|
+
[modalId]: modalComponent
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
case ACTIONS.closeModal: {
|
|
103
|
+
const { modalId, outletId } = action.payload;
|
|
104
|
+
const modalOutlet = state.outlets[outletId];
|
|
105
|
+
if (!modalOutlet) throw new Error(`Outlet with id ${outletId} not found`);
|
|
106
|
+
const { [modalId]: _removed,...restModals } = modalOutlet;
|
|
107
|
+
return {
|
|
108
|
+
...state,
|
|
109
|
+
outlets: {
|
|
110
|
+
...state.outlets,
|
|
111
|
+
[outletId]: restModals
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
case ACTIONS.addOutlet: {
|
|
116
|
+
const { outletId } = action.payload;
|
|
117
|
+
if (!!state.outlets[outletId]) throw new Error(`Outlet with id ${outletId} already exists`);
|
|
118
|
+
return {
|
|
119
|
+
...state,
|
|
120
|
+
outlets: {
|
|
121
|
+
...state.outlets,
|
|
122
|
+
[outletId]: {}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
case ACTIONS.removeOutlet: {
|
|
127
|
+
const { outletId } = action.payload;
|
|
128
|
+
const { [outletId]: _removed,...restOutlets } = state.outlets;
|
|
129
|
+
return {
|
|
130
|
+
...state,
|
|
131
|
+
outlets: restOutlets
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
default: return state;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const useRMUState = () => {
|
|
138
|
+
const [state, dispatch] = useReducer(reducer, { outlets: {} });
|
|
139
|
+
const openModal$1 = ({ modalId, modalComponent, outletId }) => {
|
|
140
|
+
dispatch({
|
|
141
|
+
type: ACTIONS.openModal,
|
|
142
|
+
payload: {
|
|
143
|
+
modalId,
|
|
144
|
+
modalComponent,
|
|
145
|
+
outletId
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const closeModal$1 = ({ modalId, outletId }) => {
|
|
150
|
+
dispatch({
|
|
151
|
+
type: ACTIONS.closeModal,
|
|
152
|
+
payload: {
|
|
153
|
+
modalId,
|
|
154
|
+
outletId
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
const addOutlet = (outletId) => {
|
|
159
|
+
dispatch({
|
|
160
|
+
type: ACTIONS.addOutlet,
|
|
161
|
+
payload: { outletId }
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
const removeOutlet = (outletId) => {
|
|
165
|
+
dispatch({
|
|
166
|
+
type: ACTIONS.removeOutlet,
|
|
167
|
+
payload: { outletId }
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
return {
|
|
171
|
+
...state,
|
|
172
|
+
openModal: openModal$1,
|
|
173
|
+
closeModal: closeModal$1,
|
|
174
|
+
addOutlet,
|
|
175
|
+
removeOutlet
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/rmu-provider.tsx
|
|
181
|
+
const RMUProvider = ({ children }) => {
|
|
182
|
+
const state = useRMUState();
|
|
183
|
+
useRMUEvents(state);
|
|
184
|
+
return /* @__PURE__ */ React.createElement(RMUContext.Provider, { value: state }, children);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region src/rmu-outlet.tsx
|
|
189
|
+
const RMUOutlet = ({ outletId = "rmu-default-outlet" }) => {
|
|
190
|
+
const ctx = useContext(RMUContext);
|
|
191
|
+
if (!ctx) throw new Error("RMUProvider not found in component tree");
|
|
192
|
+
const { outlets, addOutlet, removeOutlet } = ctx;
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
addOutlet(outletId);
|
|
195
|
+
return () => {
|
|
196
|
+
removeOutlet(outletId);
|
|
197
|
+
};
|
|
198
|
+
}, []);
|
|
199
|
+
const modals = outlets[outletId] ?? {};
|
|
200
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, Object.entries(modals).map(([modalId, modalComponent]) => /* @__PURE__ */ React.createElement(Fragment, { key: modalId }, modalComponent)));
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
//#endregion
|
|
204
|
+
export { RMUOutlet, RMUProvider, closeModal, openModal };
|
package/package.json
CHANGED
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"name": "@violice/rmu",
|
|
3
|
+
"version": "0.2.7",
|
|
4
|
+
"author": "Sergey Ivashko",
|
|
3
5
|
"license": "MIT",
|
|
4
|
-
"main": "dist/index.js",
|
|
5
|
-
"typings": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist",
|
|
8
8
|
"src"
|
|
9
9
|
],
|
|
10
10
|
"engines": {
|
|
11
|
-
"node": ">=
|
|
11
|
+
"node": ">=20"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"start": "
|
|
15
|
-
"build": "
|
|
16
|
-
"
|
|
17
|
-
"lint": "tsdx lint",
|
|
18
|
-
"prepare": "tsdx build",
|
|
14
|
+
"start": "tsdown --watch",
|
|
15
|
+
"build": "tsdown",
|
|
16
|
+
"prepare": "tsdown",
|
|
19
17
|
"size": "size-limit",
|
|
20
18
|
"analyze": "size-limit --why"
|
|
21
19
|
},
|
|
22
20
|
"peerDependencies": {
|
|
23
|
-
"react": ">=16"
|
|
21
|
+
"react": ">=16",
|
|
22
|
+
"react-native": ">=0.60"
|
|
24
23
|
},
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
24
|
+
"peerDependenciesMeta": {
|
|
25
|
+
"react-native": {
|
|
26
|
+
"optional": true
|
|
28
27
|
}
|
|
29
28
|
},
|
|
30
29
|
"prettier": {
|
|
@@ -33,29 +32,37 @@
|
|
|
33
32
|
"singleQuote": true,
|
|
34
33
|
"trailingComma": "es5"
|
|
35
34
|
},
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
35
|
+
"main": "dist/index.js",
|
|
36
|
+
"module": "dist/index.mjs",
|
|
37
|
+
"types": "dist/index.d.ts",
|
|
38
|
+
"react-native": "dist/index.js",
|
|
39
|
+
"exports": {
|
|
40
|
+
".": {
|
|
41
|
+
"types": "./dist/index.d.ts",
|
|
42
|
+
"import": "./dist/index.mjs",
|
|
43
|
+
"require": "./dist/index.js"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"sideEffects": false,
|
|
39
47
|
"size-limit": [
|
|
40
48
|
{
|
|
41
|
-
"path": "dist/
|
|
42
|
-
"limit": "
|
|
49
|
+
"path": "dist/index.js",
|
|
50
|
+
"limit": "2 KB"
|
|
43
51
|
},
|
|
44
52
|
{
|
|
45
|
-
"path": "dist/
|
|
46
|
-
"limit": "
|
|
53
|
+
"path": "dist/index.mjs",
|
|
54
|
+
"limit": "2 KB"
|
|
47
55
|
}
|
|
48
56
|
],
|
|
49
57
|
"devDependencies": {
|
|
50
|
-
"@size-limit/preset-small-lib": "^
|
|
51
|
-
"@types/react": "^18.
|
|
52
|
-
"@types/react-dom": "^18.
|
|
53
|
-
"
|
|
54
|
-
"react": "^18.
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"typescript": "^4.9.5"
|
|
58
|
+
"@size-limit/preset-small-lib": "^11.2.0",
|
|
59
|
+
"@types/react": "^18.3.25",
|
|
60
|
+
"@types/react-dom": "^18.3.7",
|
|
61
|
+
"react": "^18.3.1",
|
|
62
|
+
"react-dom": "^18.3.1",
|
|
63
|
+
"size-limit": "^11.2.0",
|
|
64
|
+
"tsdown": "^0.15.6",
|
|
65
|
+
"tslib": "^2.8.1",
|
|
66
|
+
"typescript": "^5.9.3"
|
|
60
67
|
}
|
|
61
68
|
}
|
package/src/emitter.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
type EventHandler = (payload: any) => void;
|
|
2
|
+
|
|
3
|
+
type EventListenersMap = Record<string, Set<EventHandler>>;
|
|
4
|
+
|
|
5
|
+
const listenersByType: EventListenersMap = {};
|
|
6
|
+
|
|
7
|
+
export const emitter = {
|
|
8
|
+
subscribe(type: string, handler: EventHandler) {
|
|
9
|
+
if (!listenersByType[type]) {
|
|
10
|
+
listenersByType[type] = new Set<EventHandler>();
|
|
11
|
+
}
|
|
12
|
+
listenersByType[type].add(handler);
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
unsubscribe(type: string, handler: EventHandler) {
|
|
16
|
+
const listeners = listenersByType[type];
|
|
17
|
+
if (listeners) {
|
|
18
|
+
listeners.delete(handler);
|
|
19
|
+
if (listeners.size === 0) {
|
|
20
|
+
delete listenersByType[type];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
emit(type: string, payload: any) {
|
|
26
|
+
const listeners = listenersByType[type];
|
|
27
|
+
if (!listeners) return;
|
|
28
|
+
// Clone to avoid issues if handlers mutate subscription during emit
|
|
29
|
+
[...listeners].forEach(handler => {
|
|
30
|
+
try {
|
|
31
|
+
handler(payload);
|
|
32
|
+
} catch {
|
|
33
|
+
// Swallow to ensure one handler error does not break others
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
};
|