@violice/rmu 0.2.7 → 0.2.9

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 CHANGED
@@ -1,54 +1,359 @@
1
- # RMU (React modal utility)
1
+ # RMU (React Modal Utility)
2
2
 
3
- RMU is a small, zero-dependency utility to control modals in React apps
3
+ [![npm version](https://badge.fury.io/js/@violice%2Frmu.svg)](https://www.npmjs.com/package/@violice/rmu)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
5
 
5
- <!-- ## Installation
6
+ A lightweight, zero-dependency utility for managing modals in React applications. Built with TypeScript, supports multiple outlets, and works seamlessly with React context.
7
+
8
+ ## Features
9
+
10
+ - **Zero Dependencies** - No external libraries required
11
+ - **TypeScript** - Full type safety out of the box
12
+ - **Multiple Outlets** - Render modals in different parts of your app
13
+ - **Context-Aware** - Modals have access to React context at their outlet location
14
+ - **Imperative API** - Open/close modals from anywhere without prop drilling
15
+ - **Lightweight** - < 2KB gzipped
16
+ - **React Native Support** - Works with both React DOM and React Native
17
+
18
+ ## Installation
6
19
 
7
20
  ```bash
8
- npm install --save @violice/rmu
21
+ npm install @violice/rmu
22
+ # or
9
23
  yarn add @violice/rmu
10
- ``` -->
24
+ # or
25
+ pnpm add @violice/rmu
26
+ ```
27
+
28
+ ## Requirements
29
+
30
+ - React >= 18
31
+ - Node.js >= 24
32
+
33
+ ## Quick Start
11
34
 
12
- ## Usage
35
+ ### 1. Wrap your app with `RMUProvider`
13
36
 
14
- ```js
15
- import { openModal, closeModal, RMUOutlet, RMUProvider } from '@violice/rmu';
37
+ ```tsx
38
+ import { RMUProvider } from '@violice/rmu';
16
39
 
17
- const Example = () => {
40
+ function App() {
18
41
  return (
19
42
  <RMUProvider>
20
- <button
21
- onClick={() => {
22
- const modal = openModal(
23
- <TestModal onClose={() => closeModal(modal)} />
24
- );
25
- }}
26
- >
27
- Open modal
28
- </button>
29
- <SomeContextProvider>
30
- <button onClick={() => {
31
- const modal = openModal(
32
- <TestContextModal onClose={() => closeModal(modal)} />,
33
- { outletId: 'MY_OUTLET' },
34
- );
35
- }}>
36
- Open modal with access to some context
37
- </button>
38
- <RMUOutlet outletId="MY_OUTLET">
39
- </SomeContextProvider>
40
- <RMUOutlet />
43
+ <YourApp />
41
44
  </RMUProvider>
42
45
  );
46
+ }
47
+ ```
48
+
49
+ ### 2. Add `RMUOutlet` where you want modals to render
50
+
51
+ ```tsx
52
+ import { RMUOutlet } from '@violice/rmu';
53
+
54
+ function Layout() {
55
+ return (
56
+ <div>
57
+ <main>{/* Your page content */}</main>
58
+ <RMUOutlet /> {/* Modals will render here */}
59
+ </div>
60
+ );
61
+ }
62
+ ```
63
+
64
+ ### 3. Open modals from anywhere
65
+
66
+ ```tsx
67
+ import { openModal, closeModal } from '@violice/rmu';
68
+
69
+ function MyComponent() {
70
+ const handleOpenModal = () => {
71
+ const modal = openModal(
72
+ <MyModal onClose={() => closeModal(modal)} />
73
+ );
74
+ };
75
+
76
+ return <button onClick={handleOpenModal}>Open Modal</button>;
77
+ }
78
+ ```
79
+
80
+ ## API Reference
81
+
82
+ ### Components
83
+
84
+ #### `RMUProvider`
85
+
86
+ Root provider component that must wrap your application or the part of the app that uses modals.
87
+
88
+ ```tsx
89
+ interface RMUProviderProps {
90
+ children: React.ReactNode;
91
+ }
92
+ ```
93
+
94
+ **Usage:**
95
+ ```tsx
96
+ <RMUProvider>
97
+ <App />
98
+ </RMUProvider>
99
+ ```
100
+
101
+ #### `RMUOutlet`
102
+
103
+ Component that renders modals. Multiple outlets can be used with different IDs to render modals in different locations.
104
+
105
+ ```tsx
106
+ interface RMUOutletProps {
107
+ outletId?: string; // Default: 'RMU:DEFAULT_OUTLET'
108
+ }
109
+ ```
110
+
111
+ **Usage:**
112
+ ```tsx
113
+ // Default outlet
114
+ <RMUOutlet />
115
+
116
+ // Custom outlet
117
+ <RMUOutlet outletId="sidebar-outlet" />
118
+ ```
119
+
120
+ ### Functions
121
+
122
+ #### `openModal(component, config?)`
123
+
124
+ Opens a modal and returns its identifier.
125
+
126
+ ```tsx
127
+ function openModal(
128
+ component: React.ReactNode,
129
+ config?: { outletId?: string }
130
+ ): { modalId: string; outletId: string }
131
+ ```
132
+
133
+ **Parameters:**
134
+ - `component` - React element to render as modal
135
+ - `config.outletId` - Optional outlet ID (defaults to default outlet)
136
+
137
+ **Returns:**
138
+ - `modalId` - Unique identifier for the modal
139
+ - `outletId` - The outlet where the modal is rendered
140
+
141
+ **Usage:**
142
+ ```tsx
143
+ const { modalId, outletId } = openModal(<MyModal />, {
144
+ outletId: 'custom-outlet'
145
+ });
146
+ ```
147
+
148
+ #### `closeModal({ modalId, outletId })`
149
+
150
+ Closes a specific modal by its identifier.
151
+
152
+ ```tsx
153
+ function closeModal({
154
+ modalId: string,
155
+ outletId: string
156
+ }): void
157
+ ```
158
+
159
+ **Usage:**
160
+ ```tsx
161
+ const modal = openModal(<MyModal />);
162
+ // ... later
163
+ closeModal(modal);
164
+ ```
165
+
166
+ ## Advanced Usage
167
+
168
+ ### Multiple Outlets with Context Access
169
+
170
+ One of RMU's key features is the ability to render modals in different outlets, allowing them to access React context at that location.
171
+
172
+ ```tsx
173
+ import { RMUProvider, RMUOutlet, openModal, closeModal } from '@violice/rmu';
174
+ import { ThemeContext, UserContext } from './contexts';
175
+
176
+ function App() {
177
+ return (
178
+ <RMUProvider>
179
+ <ThemeContext.Provider value="dark">
180
+ {/* Modals here have access to ThemeContext */}
181
+ <RMUOutlet outletId="theme-aware" />
182
+
183
+ <UserContext.Provider value={{ name: 'John' }}>
184
+ {/* Modals here have access to both contexts */}
185
+ <RMUOutlet outletId="user-aware" />
186
+
187
+ <button onClick={() => {
188
+ const modal = openModal(<UserProfileModal />, {
189
+ outletId: 'user-aware'
190
+ });
191
+ }}>
192
+ Open User Modal
193
+ </button>
194
+ </UserContext.Provider>
195
+ </ThemeContext.Provider>
196
+ </RMUProvider>
197
+ );
198
+ }
199
+ ```
200
+
201
+ ### Managing Multiple Modals
202
+
203
+ ```tsx
204
+ function MultipleModalsExample() {
205
+ const modalsRef = useRef<Array<{ modalId: string; outletId: string }>>([]);
206
+
207
+ const openFirstModal = () => {
208
+ const modal = openModal(
209
+ <Modal1 onClose={() => closeModal(modal)} />
210
+ );
211
+ modalsRef.current.push(modal);
212
+ };
213
+
214
+ const openSecondModal = () => {
215
+ const modal = openModal(
216
+ <Modal2 onClose={() => closeModal(modal)} />
217
+ );
218
+ modalsRef.current.push(modal);
219
+ };
220
+
221
+ const closeAllModals = () => {
222
+ modalsRef.current.forEach(modal => closeModal(modal));
223
+ modalsRef.current = [];
224
+ };
225
+
226
+ return (
227
+ <div>
228
+ <button onClick={openFirstModal}>Open Modal 1</button>
229
+ <button onClick={openSecondModal}>Open Modal 2</button>
230
+ <button onClick={closeAllModals}>Close All</button>
231
+ </div>
232
+ );
233
+ }
234
+ ```
235
+
236
+ ### Complete Modal Component Example
237
+
238
+ ```tsx
239
+ interface ConfirmModalProps {
240
+ title: string;
241
+ message: string;
242
+ onConfirm: () => void;
243
+ onClose: () => void;
244
+ }
245
+
246
+ function ConfirmModal({ title, message, onConfirm, onClose }: ConfirmModalProps) {
247
+ return (
248
+ <div className="modal-overlay" onClick={onClose}>
249
+ <div className="modal-content" onClick={e => e.stopPropagation()}>
250
+ <h2>{title}</h2>
251
+ <p>{message}</p>
252
+ <div className="modal-actions">
253
+ <button onClick={onClose}>Cancel</button>
254
+ <button onClick={() => { onConfirm(); onClose(); }}>
255
+ Confirm
256
+ </button>
257
+ </div>
258
+ </div>
259
+ </div>
260
+ );
261
+ }
262
+
263
+ // Usage
264
+ function MyComponent() {
265
+ const handleDelete = () => {
266
+ const modal = openModal(
267
+ <ConfirmModal
268
+ title="Delete Item"
269
+ message="Are you sure you want to delete this item?"
270
+ onConfirm={() => console.log('Deleted!')}
271
+ onClose={() => closeModal(modal)}
272
+ />
273
+ );
274
+ };
275
+
276
+ return <button onClick={handleDelete}>Delete</button>;
277
+ }
278
+ ```
279
+
280
+ ## How It Works
281
+
282
+ RMU uses a combination of patterns to provide its functionality:
283
+
284
+ 1. **Event Emitter** - A custom event system allows imperative `openModal()`/`closeModal()` calls from anywhere
285
+ 2. **React Context** - `RMUProvider` maintains state and provides it to the component tree
286
+ 3. **Reducer Pattern** - State updates are handled through a reducer for predictable state management
287
+ 4. **Outlet System** - Multiple named outlets allow flexible modal placement
288
+
289
+ ```
290
+ ┌─────────────────────────────────────┐
291
+ │ RMUProvider │
292
+ │ ┌───────────────────────────────┐ │
293
+ │ │ State (Reducer) │ │
294
+ │ │ outlets: { │ │
295
+ │ │ 'default': { │ │
296
+ │ │ 'modal-1': <ModalA />, │ │
297
+ │ │ 'modal-2': <ModalB /> │ │
298
+ │ │ } │ │
299
+ │ │ } │ │
300
+ │ └───────────────────────────────┘ │
301
+ │ │ │
302
+ │ ▼ │
303
+ │ ┌───────────────────────────────┐ │
304
+ │ │ RMUOutlet │ │
305
+ │ │ Renders: <ModalA /> │ │
306
+ │ │ <ModalB /> │ │
307
+ │ └───────────────────────────────┘ │
308
+ └─────────────────────────────────────┘
309
+
310
+ │ Events
311
+ openModal() / closeModal()
312
+ ```
313
+
314
+ ## TypeScript
315
+
316
+ RMU is written in TypeScript and provides full type safety.
317
+
318
+ ```tsx
319
+ import type { OpenModalPayload, CloseModalPayload } from '@violice/rmu';
320
+
321
+ // Types are available for advanced use cases
322
+ const handleOpen = (payload: OpenModalPayload) => {
323
+ console.log(payload.modalId, payload.outletId);
43
324
  };
44
325
  ```
45
326
 
46
- ## RoadMap
47
- - [x] Custom events, new API (0.2.0)
48
- - [ ] Connected modals
49
- - [ ] Auto-close
50
- - [ ] Use for toasts example
327
+ ## Contributing
328
+
329
+ Contributions are welcome! Please feel free to submit a Pull Request.
330
+
331
+ 1. Fork the repository
332
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
333
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
334
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
335
+ 5. Open a Pull Request
336
+
337
+ ## Development
338
+
339
+ ```bash
340
+ # Install dependencies
341
+ npm install
342
+
343
+ # Run tests
344
+ npm test
345
+
346
+ # Build the library
347
+ npm run build
348
+
349
+ # Check bundle size
350
+ npm run size
351
+ ```
51
352
 
52
353
  ## License
53
354
 
54
- Licensed under MIT
355
+ Licensed under [MIT](LICENSE).
356
+
357
+ ---
358
+
359
+ **Made with ❤️ by [Sergey Ivashko](https://github.com/violice)**
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`react`);c=s(c,1);let l=require(`react/jsx-runtime`);const u=(0,c.createContext)(null);let d=function(e){return e.OpenModal=`RMU:OPEN_MODAL`,e.CloseModal=`RMU:CLOSE_MODAL`,e}({});const f=`RMU:DEFAULT_OUTLET`,p={open:d.OpenModal,close:d.CloseModal},m={},h={subscribe(e,t){m[e]||(m[e]=new Set),m[e].add(t)},unsubscribe(e,t){let n=m[e];n&&(n.delete(t),n.size===0&&delete m[e])},emit(e,t){let n=m[e];n&&[...n].forEach(e=>{try{e(t)}catch{}})},_clear(){Object.keys(m).forEach(e=>{delete m[e]})}},g=e=>{let t={open:t=>e.openModal(t),close:t=>e.closeModal(t)};(0,c.useEffect)(()=>(Object.keys(t).forEach(e=>{h.subscribe(p[e],t[e])}),()=>{Object.keys(t).forEach(e=>{h.unsubscribe(p[e],t[e])})}),[])},_={openModal:`rmu:open-modal`,closeModal:`rmu:close-modal`,addOutlet:`rmu:add-modal`,removeOutlet:`rmu:remove-outlet`},v=(e,t)=>{switch(t.type){case _.openModal:{let{modalId:n,modalComponent:r,outletId:i}=t.payload,a=e.outlets[i];if(!a)throw Error(`Outlet with id ${i} not found`);return{...e,outlets:{...e.outlets,[i]:{...a,[n]:r}}}}case _.closeModal:{let{modalId:n,outletId:r}=t.payload,i=e.outlets[r];if(!i)throw Error(`Outlet with id ${r} not found`);let{[n]:a,...o}=i;return{...e,outlets:{...e.outlets,[r]:o}}}case _.addOutlet:{let{outletId:n}=t.payload;if(e.outlets[n])throw Error(`Outlet with id ${n} already exists`);return{...e,outlets:{...e.outlets,[n]:{}}}}case _.removeOutlet:{let{outletId:n}=t.payload,{[n]:r,...i}=e.outlets;return{...e,outlets:i}}default:return e}},y=()=>{let[e,t]=(0,c.useReducer)(v,{outlets:{}}),n=({modalId:e,modalComponent:n,outletId:r})=>{t({type:_.openModal,payload:{modalId:e,modalComponent:n,outletId:r}})},r=({modalId:e,outletId:n})=>{t({type:_.closeModal,payload:{modalId:e,outletId:n}})},i=e=>{t({type:_.addOutlet,payload:{outletId:e}})},a=e=>{t({type:_.removeOutlet,payload:{outletId:e}})};return{...e,openModal:n,closeModal:r,addOutlet:i,removeOutlet:a}},b=({children:e})=>{let t=y();return g(t),(0,l.jsx)(u.Provider,{value:t,children:e})},x=({outletId:e=f})=>{let t=(0,c.useContext)(u);if(!t)throw Error(`RMUProvider not found in component tree`);let{outlets:n,addOutlet:r,removeOutlet:i}=t;(0,c.useEffect)(()=>(r(e),()=>{i(e)}),[]);let a=n[e]??{};return(0,l.jsx)(l.Fragment,{children:Object.entries(a).map(([e,t])=>(0,l.jsx)(c.Fragment,{children:t},e))})},S=(e,t={})=>{let n=`rmu-modal-${new Date().getTime().toString()}`,{outletId:r=f}=t,i={modalId:n,modalComponent:e,outletId:r};return h.emit(d.OpenModal,i),{modalId:n,outletId:r}},C=({modalId:e,outletId:t})=>{let n={modalId:e,outletId:t};h.emit(d.CloseModal,n)};exports.RMUOutlet=x,exports.RMUProvider=b,exports.closeModal=C,exports.openModal=S;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["Fragment"],"sources":["../src/rmu-context.ts","../src/types.ts","../src/constants.ts","../src/emitter.ts","../src/use-rmu-events.ts","../src/use-rmu-state.ts","../src/rmu-provider.tsx","../src/rmu-outlet.tsx","../src/events.ts"],"sourcesContent":["import { createContext } from 'react';\nimport { RMUContextState } from './types';\n\nexport const RMUContext = createContext<RMUContextState | null>(null);\n","import { ReactNode } from 'react';\n\nexport enum RMUEventType {\n OpenModal = 'RMU:OPEN_MODAL',\n CloseModal = 'RMU:CLOSE_MODAL',\n}\n\nexport type RMUContextState = {\n outlets: Record<string, Record<string, ReactNode>>;\n openModal: (payload: OpenModalPayload) => void;\n closeModal: (payload: CloseModalPayload) => void;\n addOutlet: (outletId: string) => void;\n removeOutlet: (outletId: string) => void;\n};\n\nexport type OpenModalPayload = {\n modalId: string;\n modalComponent: ReactNode;\n outletId: string;\n};\n\nexport type CloseModalPayload = {\n modalId: string;\n outletId: string;\n};\n\nexport type RMUEventPayload = OpenModalPayload | CloseModalPayload;\n","import { RMUEventType } from \"./types\";\n\nexport const RMU_DEFAULT_OUTLET_ID = 'RMU:DEFAULT_OUTLET' as const;\n\nexport const RMU_EVENTS = {\n open: RMUEventType.OpenModal,\n close: RMUEventType.CloseModal,\n} as const;\n","export type EventHandler<T = unknown> = (payload: T) => void;\n\ntype EventListenersMap = Record<string, Set<EventHandler<unknown>>>;\n\nconst listenersByType: EventListenersMap = {};\n\nexport const emitter = {\n subscribe<T>(type: string, handler: EventHandler<T>) {\n if (!listenersByType[type]) {\n listenersByType[type] = new Set<EventHandler<unknown>>();\n }\n listenersByType[type].add(handler as EventHandler<unknown>);\n },\n\n unsubscribe<T>(type: string, handler: EventHandler<T>) {\n const listeners = listenersByType[type];\n if (listeners) {\n listeners.delete(handler as EventHandler<unknown>);\n if (listeners.size === 0) {\n delete listenersByType[type];\n }\n }\n },\n\n emit<T>(type: string, payload: T) {\n const listeners = listenersByType[type];\n if (listeners) {\n // Clone to avoid issues if handlers mutate subscription during emit\n [...listeners].forEach((handler) => {\n try {\n handler(payload);\n } catch {\n // Swallow to ensure one handler error does not break others\n }\n });\n }\n },\n\n // For testing purposes only\n _clear() {\n Object.keys(listenersByType).forEach((key) => {\n delete listenersByType[key];\n });\n },\n};\n","import { useEffect } from 'react';\nimport { RMUContextState, OpenModalPayload, CloseModalPayload } from './types';\nimport { RMU_EVENTS } from './constants';\nimport { emitter } from './emitter';\n\nexport const useRMUEvents = (ctx: RMUContextState) => {\n const events = {\n open: (payload: OpenModalPayload) => ctx.openModal(payload),\n close: (payload: CloseModalPayload) => ctx.closeModal(payload),\n };\n\n useEffect(() => {\n (Object.keys(events) as (keyof typeof events)[]).forEach(event => {\n emitter.subscribe(RMU_EVENTS[event], events[event]);\n });\n\n return () => {\n (Object.keys(events) as (keyof typeof events)[]).forEach(event => {\n emitter.unsubscribe(RMU_EVENTS[event], events[event]);\n });\n };\n }, []);\n};\n","import { useReducer } from 'react';\nimport { RMUContextState } from './types';\n\nconst ACTIONS = {\n openModal: 'rmu:open-modal',\n closeModal: 'rmu:close-modal',\n addOutlet: 'rmu:add-modal',\n removeOutlet: 'rmu:remove-outlet',\n} as const;\n\nconst reducer = (\n state: {\n outlets: RMUContextState['outlets'];\n },\n action: {\n type: string;\n payload: Record<string, any>;\n }\n) => {\n switch (action.type) {\n case ACTIONS.openModal: {\n const { modalId, modalComponent, outletId } = action.payload;\n const modalOutlet = state.outlets[outletId];\n\n if (!modalOutlet) {\n throw new Error(`Outlet with id ${outletId} not found`);\n }\n\n return {\n ...state,\n outlets: {\n ...state.outlets,\n [outletId]: {\n ...modalOutlet,\n [modalId]: modalComponent,\n },\n },\n };\n }\n case ACTIONS.closeModal: {\n const { modalId, outletId } = action.payload;\n\n const modalOutlet = state.outlets[outletId];\n if (!modalOutlet) {\n throw new Error(`Outlet with id ${outletId} not found`);\n }\n\n const { [modalId]: _removed, ...restModals } = modalOutlet;\n\n return {\n ...state,\n outlets: {\n ...state.outlets,\n [outletId]: restModals,\n },\n };\n }\n case ACTIONS.addOutlet: {\n const { outletId } = action.payload;\n\n if (!!state.outlets[outletId]) {\n throw new Error(`Outlet with id ${outletId} already exists`);\n }\n\n return {\n ...state,\n outlets: {\n ...state.outlets,\n [outletId]: {},\n },\n };\n }\n case ACTIONS.removeOutlet: {\n const { outletId } = action.payload;\n\n const { [outletId]: _removed, ...restOutlets } = state.outlets;\n\n return {\n ...state,\n outlets: restOutlets,\n };\n }\n default:\n return state;\n }\n};\n\nexport const useRMUState = () => {\n const [state, dispatch] = useReducer(reducer, {\n outlets: {},\n });\n\n const openModal: RMUContextState['openModal'] = ({\n modalId,\n modalComponent,\n outletId,\n }) => {\n dispatch({\n type: ACTIONS.openModal,\n payload: { modalId, modalComponent, outletId },\n });\n };\n\n const closeModal: RMUContextState['closeModal'] = ({ modalId, outletId }) => {\n dispatch({ type: ACTIONS.closeModal, payload: { modalId, outletId } });\n };\n\n const addOutlet: RMUContextState['addOutlet'] = outletId => {\n dispatch({ type: ACTIONS.addOutlet, payload: { outletId } });\n };\n\n const removeOutlet: RMUContextState['removeOutlet'] = outletId => {\n dispatch({ type: ACTIONS.removeOutlet, payload: { outletId } });\n };\n\n return {\n ...state,\n openModal,\n closeModal,\n addOutlet,\n removeOutlet,\n };\n};\n","import React from 'react';\nimport { RMUContext } from './rmu-context';\nimport { useRMUEvents } from './use-rmu-events';\nimport { useRMUState } from './use-rmu-state';\n\nexport const RMUProvider = ({ children }: { children: React.ReactNode }) => {\n const state = useRMUState();\n\n useRMUEvents(state);\n\n return <RMUContext.Provider value={state}>{children}</RMUContext.Provider>;\n};\n","import React, { Fragment, useContext, useEffect } from 'react';\nimport { RMUContext } from './rmu-context';\nimport { RMU_DEFAULT_OUTLET_ID } from './constants';\n\nexport const RMUOutlet = ({ outletId = RMU_DEFAULT_OUTLET_ID }) => {\n const ctx = useContext(RMUContext);\n\n if (!ctx) {\n throw new Error('RMUProvider not found in component tree');\n }\n\n const { outlets, addOutlet, removeOutlet } = ctx;\n\n useEffect(() => {\n addOutlet(outletId);\n return () => {\n removeOutlet(outletId);\n };\n }, []);\n\n const modals = outlets[outletId] ?? {};\n\n return (\n <>\n {Object.entries(modals).map(([modalId, modalComponent]) => (\n <Fragment key={modalId}>{modalComponent}</Fragment>\n ))}\n </>\n );\n};\n\nexport default RMUOutlet;\n","import { ReactNode } from 'react';\nimport { OpenModalPayload, CloseModalPayload, RMUEventType } from './types';\nimport { emitter } from './emitter';\nimport { RMU_DEFAULT_OUTLET_ID } from './constants';\n\nexport const openModal = (\n modalComponent: ReactNode,\n config: { outletId?: string } = {}\n): { modalId: string; outletId: string } => {\n const modalId = `rmu-modal-${new Date().getTime().toString()}`;\n const { outletId = RMU_DEFAULT_OUTLET_ID } = config;\n const payload: OpenModalPayload = {\n modalId,\n modalComponent,\n outletId,\n };\n emitter.emit<OpenModalPayload>(RMUEventType.OpenModal, payload);\n return { modalId, outletId };\n};\n\nexport const closeModal = ({\n modalId,\n outletId,\n}: {\n modalId: string;\n outletId: string;\n}): void => {\n const payload: CloseModalPayload = { modalId, outletId };\n emitter.emit<CloseModalPayload>(RMUEventType.CloseModal, payload);\n};\n"],"mappings":"omBAGA,MAAa,GAAA,EAAA,EAAA,eAAmD,KAAK,CCDrE,IAAY,EAAL,SAAA,EAAA,OACL,GAAA,UAAA,iBACA,EAAA,WAAA,wBACD,CCHD,MAAa,EAAwB,qBAExB,EAAa,CACxB,KAAM,EAAa,UACnB,MAAO,EAAa,WACrB,CCHK,EAAqC,EAAE,CAEhC,EAAU,CACrB,UAAa,EAAc,EAA0B,CAC9C,EAAgB,KACnB,EAAgB,GAAQ,IAAI,KAE9B,EAAgB,GAAM,IAAI,EAAiC,EAG7D,YAAe,EAAc,EAA0B,CACrD,IAAM,EAAY,EAAgB,GAC9B,IACF,EAAU,OAAO,EAAiC,CAC9C,EAAU,OAAS,GACrB,OAAO,EAAgB,KAK7B,KAAQ,EAAc,EAAY,CAChC,IAAM,EAAY,EAAgB,GAC9B,GAEF,CAAC,GAAG,EAAU,CAAC,QAAS,GAAY,CAClC,GAAI,CACF,EAAQ,EAAQ,MACV,IAGR,EAKN,QAAS,CACP,OAAO,KAAK,EAAgB,CAAC,QAAS,GAAQ,CAC5C,OAAO,EAAgB,IACvB,EAEL,CCvCY,EAAgB,GAAyB,CACpD,IAAM,EAAS,CACb,KAAO,GAA8B,EAAI,UAAU,EAAQ,CAC3D,MAAQ,GAA+B,EAAI,WAAW,EAAQ,CAC/D,EAED,EAAA,EAAA,gBACG,OAAO,KAAK,EAAO,CAA6B,QAAQ,GAAS,CAChE,EAAQ,UAAU,EAAW,GAAQ,EAAO,GAAO,EACnD,KAEW,CACV,OAAO,KAAK,EAAO,CAA6B,QAAQ,GAAS,CAChE,EAAQ,YAAY,EAAW,GAAQ,EAAO,GAAO,EACrD,GAEH,EAAE,CAAC,EClBF,EAAU,CACd,UAAW,iBACX,WAAY,kBACZ,UAAW,gBACX,aAAc,oBACf,CAEK,GACJ,EAGA,IAIG,CACH,OAAQ,EAAO,KAAf,CACE,KAAK,EAAQ,UAAW,CACtB,GAAM,CAAE,UAAS,iBAAgB,YAAa,EAAO,QAC/C,EAAc,EAAM,QAAQ,GAElC,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAAS,YAAY,CAGzD,MAAO,CACL,GAAG,EACH,QAAS,CACP,GAAG,EAAM,SACR,GAAW,CACV,GAAG,GACF,GAAU,EACZ,CACF,CACF,CAEH,KAAK,EAAQ,WAAY,CACvB,GAAM,CAAE,UAAS,YAAa,EAAO,QAE/B,EAAc,EAAM,QAAQ,GAClC,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAAS,YAAY,CAGzD,GAAM,EAAG,GAAU,EAAU,GAAG,GAAe,EAE/C,MAAO,CACL,GAAG,EACH,QAAS,CACP,GAAG,EAAM,SACR,GAAW,EACb,CACF,CAEH,KAAK,EAAQ,UAAW,CACtB,GAAM,CAAE,YAAa,EAAO,QAE5B,GAAM,EAAM,QAAQ,GAClB,MAAU,MAAM,kBAAkB,EAAS,iBAAiB,CAG9D,MAAO,CACL,GAAG,EACH,QAAS,CACP,GAAG,EAAM,SACR,GAAW,EAAE,CACf,CACF,CAEH,KAAK,EAAQ,aAAc,CACzB,GAAM,CAAE,YAAa,EAAO,QAEtB,EAAG,GAAW,EAAU,GAAG,GAAgB,EAAM,QAEvD,MAAO,CACL,GAAG,EACH,QAAS,EACV,CAEH,QACE,OAAO,IAIA,MAAoB,CAC/B,GAAM,CAAC,EAAO,IAAA,EAAA,EAAA,YAAuB,EAAS,CAC5C,QAAS,EAAE,CACZ,CAAC,CAEI,GAA2C,CAC/C,UACA,iBACA,cACI,CACJ,EAAS,CACP,KAAM,EAAQ,UACd,QAAS,CAAE,UAAS,iBAAgB,WAAU,CAC/C,CAAC,EAGE,GAA6C,CAAE,UAAS,cAAe,CAC3E,EAAS,CAAE,KAAM,EAAQ,WAAY,QAAS,CAAE,UAAS,WAAU,CAAE,CAAC,EAGlE,EAA0C,GAAY,CAC1D,EAAS,CAAE,KAAM,EAAQ,UAAW,QAAS,CAAE,WAAU,CAAE,CAAC,EAGxD,EAAgD,GAAY,CAChE,EAAS,CAAE,KAAM,EAAQ,aAAc,QAAS,CAAE,WAAU,CAAE,CAAC,EAGjE,MAAO,CACL,GAAG,EACH,YACA,aACA,YACA,eACD,ECpHU,GAAe,CAAE,cAA8C,CAC1E,IAAM,EAAQ,GAAa,CAI3B,OAFA,EAAa,EAAM,EAEZ,EAAA,EAAA,KAAC,EAAW,SAAZ,CAAqB,MAAO,EAAQ,WAA+B,CAAA,ECN/D,GAAa,CAAE,WAAW,KAA4B,CACjE,IAAM,GAAA,EAAA,EAAA,YAAiB,EAAW,CAElC,GAAI,CAAC,EACH,MAAU,MAAM,0CAA0C,CAG5D,GAAM,CAAE,UAAS,YAAW,gBAAiB,GAE7C,EAAA,EAAA,gBACE,EAAU,EAAS,KACN,CACX,EAAa,EAAS,GAEvB,EAAE,CAAC,CAEN,IAAM,EAAS,EAAQ,IAAa,EAAE,CAEtC,OACE,EAAA,EAAA,KAAA,EAAA,SAAA,CAAA,SACG,OAAO,QAAQ,EAAO,CAAC,KAAK,CAAC,EAAS,MACrC,EAAA,EAAA,KAACA,EAAAA,SAAD,CAAA,SAAyB,EAA0B,CAApC,EAAoC,CACnD,CACD,CAAA,ECtBM,GACX,EACA,EAAgC,EAAE,GACQ,CAC1C,IAAM,EAAU,aAAa,IAAI,MAAM,CAAC,SAAS,CAAC,UAAU,GACtD,CAAE,WAAW,GAA0B,EACvC,EAA4B,CAChC,UACA,iBACA,WACD,CAED,OADA,EAAQ,KAAuB,EAAa,UAAW,EAAQ,CACxD,CAAE,UAAS,WAAU,EAGjB,GAAc,CACzB,UACA,cAIU,CACV,IAAM,EAA6B,CAAE,UAAS,WAAU,CACxD,EAAQ,KAAwB,EAAa,WAAY,EAAQ"}
@@ -0,0 +1,34 @@
1
+ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
2
+ import React, { ReactNode } from "react";
3
+
4
+ //#region src/rmu-provider.d.ts
5
+ declare const RMUProvider: ({
6
+ children
7
+ }: {
8
+ children: React.ReactNode;
9
+ }) => _$react_jsx_runtime0.JSX.Element;
10
+ //#endregion
11
+ //#region src/rmu-outlet.d.ts
12
+ declare const RMUOutlet: ({
13
+ outletId
14
+ }: {
15
+ outletId?: "RMU:DEFAULT_OUTLET" | undefined;
16
+ }) => _$react_jsx_runtime0.JSX.Element;
17
+ //#endregion
18
+ //#region src/events.d.ts
19
+ declare const openModal: (modalComponent: ReactNode, config?: {
20
+ outletId?: string;
21
+ }) => {
22
+ modalId: string;
23
+ outletId: string;
24
+ };
25
+ declare const closeModal: ({
26
+ modalId,
27
+ outletId
28
+ }: {
29
+ modalId: string;
30
+ outletId: string;
31
+ }) => void;
32
+ //#endregion
33
+ export { RMUOutlet, RMUProvider, closeModal, openModal };
34
+ //# sourceMappingURL=index.d.cts.map
package/dist/index.d.mts CHANGED
@@ -1,41 +1,19 @@
1
1
  import React, { ReactNode } from "react";
2
+ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
2
3
 
3
4
  //#region src/rmu-provider.d.ts
4
5
  declare const RMUProvider: ({
5
6
  children
6
7
  }: {
7
8
  children: React.ReactNode;
8
- }) => React.JSX.Element;
9
+ }) => _$react_jsx_runtime0.JSX.Element;
9
10
  //#endregion
10
11
  //#region src/rmu-outlet.d.ts
11
12
  declare const RMUOutlet: ({
12
13
  outletId
13
14
  }: {
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
- };
15
+ outletId?: "RMU:DEFAULT_OUTLET" | undefined;
16
+ }) => _$react_jsx_runtime0.JSX.Element;
39
17
  //#endregion
40
18
  //#region src/events.d.ts
41
19
  declare const openModal: (modalComponent: ReactNode, config?: {
@@ -44,6 +22,13 @@ declare const openModal: (modalComponent: ReactNode, config?: {
44
22
  modalId: string;
45
23
  outletId: string;
46
24
  };
47
- declare const closeModal: RMUContextState['closeModal'];
25
+ declare const closeModal: ({
26
+ modalId,
27
+ outletId
28
+ }: {
29
+ modalId: string;
30
+ outletId: string;
31
+ }) => void;
48
32
  //#endregion
49
- export { RMUOutlet, RMUProvider, closeModal, openModal };
33
+ export { RMUOutlet, RMUProvider, closeModal, openModal };
34
+ //# sourceMappingURL=index.d.mts.map