crosshook 1.0.0
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 +255 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# crosshook
|
|
2
|
+
|
|
3
|
+
> Tiny hook-first event bridge for decoupled React component
|
|
4
|
+
> communication.
|
|
5
|
+
|
|
6
|
+
`crosshook` lets React components communicate without prop drilling,
|
|
7
|
+
context nesting, or heavyweight state managers.
|
|
8
|
+
|
|
9
|
+
Perfect for UI orchestration like modals, toasts, loaders, and
|
|
10
|
+
notifications.
|
|
11
|
+
|
|
12
|
+
------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
## ✨ Features
|
|
15
|
+
|
|
16
|
+
✅ Hook-first API\
|
|
17
|
+
✅ No providers required\
|
|
18
|
+
✅ Tiny footprint\
|
|
19
|
+
✅ Lifecycle-safe (auto cleanup)\
|
|
20
|
+
✅ Strict Mode friendly\
|
|
21
|
+
✅ TypeScript ready
|
|
22
|
+
|
|
23
|
+
------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
## 📦 Installation
|
|
26
|
+
|
|
27
|
+
``` bash
|
|
28
|
+
npm install crosshook
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
or
|
|
32
|
+
|
|
33
|
+
``` bash
|
|
34
|
+
yarn add crosshook
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
## 🚀 Quick Start
|
|
40
|
+
|
|
41
|
+
### ✅ Register a Listener
|
|
42
|
+
|
|
43
|
+
``` tsx
|
|
44
|
+
import { useRegister } from "crosshook";
|
|
45
|
+
|
|
46
|
+
function Toast() {
|
|
47
|
+
useRegister("toast.show", (payload: { message: string }) => {
|
|
48
|
+
console.log(payload.message);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
### ✅ Call an Event
|
|
58
|
+
|
|
59
|
+
``` tsx
|
|
60
|
+
import { useBridge } from "crosshook";
|
|
61
|
+
|
|
62
|
+
function SaveButton() {
|
|
63
|
+
const { call } = useBridge();
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<button
|
|
67
|
+
onClick={() =>
|
|
68
|
+
call("toast.show", { message: "Saved successfully!" })
|
|
69
|
+
}
|
|
70
|
+
>
|
|
71
|
+
Save
|
|
72
|
+
</button>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
## 🎯 Common Use Cases
|
|
80
|
+
|
|
81
|
+
### 🔔 Toast Notifications
|
|
82
|
+
|
|
83
|
+
``` tsx
|
|
84
|
+
useRegister("toast.show", ({ message }) => {
|
|
85
|
+
setToast(message);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
call("toast.show", { message: "Profile updated!" });
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
### 🪟 Modal Control
|
|
94
|
+
|
|
95
|
+
``` tsx
|
|
96
|
+
useRegister("modal.open", () => setOpen(true));
|
|
97
|
+
useRegister("modal.close", () => setOpen(false));
|
|
98
|
+
|
|
99
|
+
call("modal.open");
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
### ⏳ Global Loader
|
|
105
|
+
|
|
106
|
+
``` tsx
|
|
107
|
+
useRegister("loader.show", () => setLoading(true));
|
|
108
|
+
useRegister("loader.hide", () => setLoading(false));
|
|
109
|
+
|
|
110
|
+
call("loader.show");
|
|
111
|
+
call("loader.hide");
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
## 🧠 API
|
|
117
|
+
|
|
118
|
+
### `useBridge()`
|
|
119
|
+
|
|
120
|
+
Returns bridge actions.
|
|
121
|
+
|
|
122
|
+
``` ts
|
|
123
|
+
const { call } = useBridge();
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### `call(eventName, payload?)`
|
|
127
|
+
|
|
128
|
+
Triggers all listeners registered for the event.
|
|
129
|
+
|
|
130
|
+
------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
### `useRegister(eventName, handler)`
|
|
133
|
+
|
|
134
|
+
Registers a listener.
|
|
135
|
+
|
|
136
|
+
``` ts
|
|
137
|
+
useRegister("event.name", (payload) => {
|
|
138
|
+
...
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
✔ Automatically cleaned up on unmount\
|
|
143
|
+
✔ Safe in React Strict Mode
|
|
144
|
+
|
|
145
|
+
------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
### `call(eventName, payload?)`
|
|
148
|
+
|
|
149
|
+
Non-hook version (can be used outside React).
|
|
150
|
+
|
|
151
|
+
``` ts
|
|
152
|
+
import { call } from "crosshook";
|
|
153
|
+
|
|
154
|
+
call("toast.show", { message: "Hello!" });
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
### `register(eventName, handler)`
|
|
160
|
+
|
|
161
|
+
Manual registration.
|
|
162
|
+
|
|
163
|
+
``` ts
|
|
164
|
+
import { register } from "crosshook";
|
|
165
|
+
|
|
166
|
+
const unregister = register("event", handler);
|
|
167
|
+
|
|
168
|
+
// later
|
|
169
|
+
unregister();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
------------------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
## ⚠️ Best Practices
|
|
175
|
+
|
|
176
|
+
### ✅ Good For
|
|
177
|
+
|
|
178
|
+
✔ UI interactions\
|
|
179
|
+
✔ Modals\
|
|
180
|
+
✔ Toasts\
|
|
181
|
+
✔ Notifications\
|
|
182
|
+
✔ Cross-tree communication
|
|
183
|
+
|
|
184
|
+
------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
### ❌ Avoid Using For
|
|
187
|
+
|
|
188
|
+
✖ Business logic orchestration\
|
|
189
|
+
✖ App state management\
|
|
190
|
+
✖ Server/cache state\
|
|
191
|
+
✖ Data persistence
|
|
192
|
+
|
|
193
|
+
Use React state / Zustand / Redux instead.
|
|
194
|
+
|
|
195
|
+
------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
## 🔷 TypeScript Usage (Optional)
|
|
198
|
+
|
|
199
|
+
You can strongly type events:
|
|
200
|
+
|
|
201
|
+
``` ts
|
|
202
|
+
type Events = {
|
|
203
|
+
"toast.show": { message: string };
|
|
204
|
+
"modal.open": void;
|
|
205
|
+
};
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
(Advanced typed helpers coming soon)
|
|
209
|
+
|
|
210
|
+
------------------------------------------------------------------------
|
|
211
|
+
|
|
212
|
+
## 🧹 Cleanup Behavior
|
|
213
|
+
|
|
214
|
+
Listeners automatically unsubscribe when components unmount.
|
|
215
|
+
|
|
216
|
+
No memory leaks. No manual cleanup required.
|
|
217
|
+
|
|
218
|
+
------------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
## 🛠 Example Pattern
|
|
221
|
+
|
|
222
|
+
``` tsx
|
|
223
|
+
// Listener
|
|
224
|
+
useRegister("user.select", (user) => {
|
|
225
|
+
setUser(user);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Caller
|
|
229
|
+
call("user.select", { id: 1, name: "Manu" });
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
------------------------------------------------------------------------
|
|
233
|
+
|
|
234
|
+
## 📏 Bundle Size
|
|
235
|
+
|
|
236
|
+
Designed to be extremely lightweight.
|
|
237
|
+
|
|
238
|
+
------------------------------------------------------------------------
|
|
239
|
+
|
|
240
|
+
## 📜 License
|
|
241
|
+
|
|
242
|
+
MIT
|
|
243
|
+
|
|
244
|
+
------------------------------------------------------------------------
|
|
245
|
+
|
|
246
|
+
## ❤️ Why crosshook?
|
|
247
|
+
|
|
248
|
+
Because not every interaction needs:
|
|
249
|
+
|
|
250
|
+
❌ Prop drilling\
|
|
251
|
+
❌ Deep context trees\
|
|
252
|
+
❌ Heavy state libraries
|
|
253
|
+
|
|
254
|
+
Sometimes you just need a tiny event bridge.
|
|
255
|
+
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type Handler<T = any> = (payload: T) => void;
|
|
2
|
+
declare function register<T = any>(event: string, handler: Handler<T>): () => void;
|
|
3
|
+
declare function call<T = any>(event: string, payload?: T): void;
|
|
4
|
+
|
|
5
|
+
declare function useRegister<T = any>(event: string, handler: Handler<T>): void;
|
|
6
|
+
declare function useBridge(): {
|
|
7
|
+
call: typeof call;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export { type Handler, call, register, useBridge, useRegister };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type Handler<T = any> = (payload: T) => void;
|
|
2
|
+
declare function register<T = any>(event: string, handler: Handler<T>): () => void;
|
|
3
|
+
declare function call<T = any>(event: string, payload?: T): void;
|
|
4
|
+
|
|
5
|
+
declare function useRegister<T = any>(event: string, handler: Handler<T>): void;
|
|
6
|
+
declare function useBridge(): {
|
|
7
|
+
call: typeof call;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export { type Handler, call, register, useBridge, useRegister };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var l=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var u=Object.prototype.hasOwnProperty;var x=(r,e)=>{for(var t in e)l(r,t,{get:e[t],enumerable:!0})},y=(r,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of p(e))!u.call(r,o)&&o!==t&&l(r,o,{get:()=>e[o],enumerable:!(s=f(e,o))||s.enumerable});return r};var H=r=>y(l({},"__esModule",{value:!0}),r);var T={};x(T,{call:()=>a,register:()=>i,useBridge:()=>c,useRegister:()=>g});module.exports=H(T);var d=require("react");var n=new Map;function i(r,e){return n.has(r)||n.set(r,new Set),n.get(r).add(e),()=>{n.get(r)?.delete(e),n.get(r)?.size===0&&n.delete(r)}}function a(r,e){let t=n.get(r);if(!t){process.env.NODE_ENV!=="production"&&console.warn(`[Bridge] No listeners for event "${r}"`);return}t.forEach(s=>{try{s(e)}catch(o){console.error(`[Bridge] Error in handler for "${r}"`,o)}})}function g(r,e){(0,d.useEffect)(()=>i(r,e),[r,e])}function c(){return{call:a}}0&&(module.exports={call,register,useBridge,useRegister});
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/hooks.ts","../src/bridge.ts"],"sourcesContent":["export { useBridge, useRegister } from \"./hooks\";\nexport { call, register } from \"./bridge\";\nexport type { Handler } from \"./bridge\";","import { useEffect } from \"react\";\nimport { register, call, Handler } from \"./bridge\";\n\nexport function useRegister<T = any>(\n event: string,\n handler: Handler<T>\n) {\n useEffect(() => {\n const unregister = register(event, handler);\n return unregister;\n }, [event, handler]);\n}\n\nexport function useBridge() {\n return { call };\n}","export type Handler<T = any> = (payload: T) => void;\n\nconst listeners = new Map<string, Set<Handler>>();\n\nexport function register<T = any>(\n event: string,\n handler: Handler<T>\n) {\n if (!listeners.has(event)) {\n listeners.set(event, new Set());\n }\n\n listeners.get(event)!.add(handler as Handler);\n\n return () => {\n listeners.get(event)?.delete(handler as Handler);\n\n if (listeners.get(event)?.size === 0) {\n listeners.delete(event);\n }\n };\n}\n\nexport function call<T = any>(event: string, payload?: T) {\n const handlers = listeners.get(event);\n\n if (!handlers) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[Bridge] No listeners for event \"${event}\"`\n );\n }\n return;\n }\n\n handlers.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(\n `[Bridge] Error in handler for \"${event}\"`,\n err\n );\n }\n });\n}"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,UAAAE,EAAA,aAAAC,EAAA,cAAAC,EAAA,gBAAAC,IAAA,eAAAC,EAAAN,GCAA,IAAAO,EAA0B,iBCE1B,IAAMC,EAAY,IAAI,IAEf,SAASC,EACZC,EACAC,EACF,CACE,OAAKH,EAAU,IAAIE,CAAK,GACpBF,EAAU,IAAIE,EAAO,IAAI,GAAK,EAGlCF,EAAU,IAAIE,CAAK,EAAG,IAAIC,CAAkB,EAErC,IAAM,CACTH,EAAU,IAAIE,CAAK,GAAG,OAAOC,CAAkB,EAE3CH,EAAU,IAAIE,CAAK,GAAG,OAAS,GAC/BF,EAAU,OAAOE,CAAK,CAE9B,CACJ,CAEO,SAASE,EAAcF,EAAeG,EAAa,CACtD,IAAMC,EAAWN,EAAU,IAAIE,CAAK,EAEpC,GAAI,CAACI,EAAU,CACP,QAAQ,IAAI,WAAa,cACzB,QAAQ,KACJ,oCAAoCJ,CAAK,GAC7C,EAEJ,MACJ,CAEAI,EAAS,QAASH,GAAY,CAC1B,GAAI,CACAA,EAAQE,CAAO,CACnB,OAASE,EAAK,CACV,QAAQ,MACJ,kCAAkCL,CAAK,IACvCK,CACJ,CACJ,CACJ,CAAC,CACL,CD1CO,SAASC,EACZC,EACAC,EACF,IACE,aAAU,IACaC,EAASF,EAAOC,CAAO,EAE3C,CAACD,EAAOC,CAAO,CAAC,CACvB,CAEO,SAASE,GAAY,CACxB,MAAO,CAAE,KAAAC,CAAK,CAClB","names":["index_exports","__export","call","register","useBridge","useRegister","__toCommonJS","import_react","listeners","register","event","handler","call","payload","handlers","err","useRegister","event","handler","register","useBridge","call"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{useEffect as l}from"react";var t=new Map;function n(r,e){return t.has(r)||t.set(r,new Set),t.get(r).add(e),()=>{t.get(r)?.delete(e),t.get(r)?.size===0&&t.delete(r)}}function s(r,e){let o=t.get(r);if(!o){process.env.NODE_ENV!=="production"&&console.warn(`[Bridge] No listeners for event "${r}"`);return}o.forEach(i=>{try{i(e)}catch(a){console.error(`[Bridge] Error in handler for "${r}"`,a)}})}function d(r,e){l(()=>n(r,e),[r,e])}function g(){return{call:s}}export{s as call,n as register,g as useBridge,d as useRegister};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks.ts","../src/bridge.ts"],"sourcesContent":["import { useEffect } from \"react\";\nimport { register, call, Handler } from \"./bridge\";\n\nexport function useRegister<T = any>(\n event: string,\n handler: Handler<T>\n) {\n useEffect(() => {\n const unregister = register(event, handler);\n return unregister;\n }, [event, handler]);\n}\n\nexport function useBridge() {\n return { call };\n}","export type Handler<T = any> = (payload: T) => void;\n\nconst listeners = new Map<string, Set<Handler>>();\n\nexport function register<T = any>(\n event: string,\n handler: Handler<T>\n) {\n if (!listeners.has(event)) {\n listeners.set(event, new Set());\n }\n\n listeners.get(event)!.add(handler as Handler);\n\n return () => {\n listeners.get(event)?.delete(handler as Handler);\n\n if (listeners.get(event)?.size === 0) {\n listeners.delete(event);\n }\n };\n}\n\nexport function call<T = any>(event: string, payload?: T) {\n const handlers = listeners.get(event);\n\n if (!handlers) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[Bridge] No listeners for event \"${event}\"`\n );\n }\n return;\n }\n\n handlers.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(\n `[Bridge] Error in handler for \"${event}\"`,\n err\n );\n }\n });\n}"],"mappings":"AAAA,OAAS,aAAAA,MAAiB,QCE1B,IAAMC,EAAY,IAAI,IAEf,SAASC,EACZC,EACAC,EACF,CACE,OAAKH,EAAU,IAAIE,CAAK,GACpBF,EAAU,IAAIE,EAAO,IAAI,GAAK,EAGlCF,EAAU,IAAIE,CAAK,EAAG,IAAIC,CAAkB,EAErC,IAAM,CACTH,EAAU,IAAIE,CAAK,GAAG,OAAOC,CAAkB,EAE3CH,EAAU,IAAIE,CAAK,GAAG,OAAS,GAC/BF,EAAU,OAAOE,CAAK,CAE9B,CACJ,CAEO,SAASE,EAAcF,EAAeG,EAAa,CACtD,IAAMC,EAAWN,EAAU,IAAIE,CAAK,EAEpC,GAAI,CAACI,EAAU,CACP,QAAQ,IAAI,WAAa,cACzB,QAAQ,KACJ,oCAAoCJ,CAAK,GAC7C,EAEJ,MACJ,CAEAI,EAAS,QAASH,GAAY,CAC1B,GAAI,CACAA,EAAQE,CAAO,CACnB,OAASE,EAAK,CACV,QAAQ,MACJ,kCAAkCL,CAAK,IACvCK,CACJ,CACJ,CACJ,CAAC,CACL,CD1CO,SAASC,EACZC,EACAC,EACF,CACEC,EAAU,IACaC,EAASH,EAAOC,CAAO,EAE3C,CAACD,EAAOC,CAAO,CAAC,CACvB,CAEO,SAASG,GAAY,CACxB,MAAO,CAAE,KAAAC,CAAK,CAClB","names":["useEffect","listeners","register","event","handler","call","payload","handlers","err","useRegister","event","handler","useEffect","register","useBridge","call"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "crosshook",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Tiny hook-based event bridge for React",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup",
|
|
13
|
+
"dev": "tsup --watch"
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"react": ">=17"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^25.3.0",
|
|
20
|
+
"@types/react": "^19.2.14",
|
|
21
|
+
"tsup": "^8.0.0",
|
|
22
|
+
"typescript": "^5.0.0"
|
|
23
|
+
}
|
|
24
|
+
}
|