react-broadcast-sync 1.0.0 → 1.1.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/.eslintignore +5 -0
- package/.github/workflows/ci.yml +3 -3
- package/.github/workflows/release.yml +39 -0
- package/.husky/pre-commit +0 -1
- package/.nvmrc +1 -0
- package/.prettierignore +1 -0
- package/.prettierrc +5 -1
- package/CHANGELOG.md +20 -0
- package/README.md +118 -47
- package/demo/react-broadcast-sync-demo/README.md +130 -0
- package/demo/react-broadcast-sync-demo/index.html +14 -0
- package/demo/react-broadcast-sync-demo/package-lock.json +6379 -0
- package/demo/react-broadcast-sync-demo/package.json +42 -0
- package/demo/react-broadcast-sync-demo/public/assets/react-broadcast-sync-logo.png +0 -0
- package/demo/react-broadcast-sync-demo/src/App.tsx +277 -0
- package/demo/react-broadcast-sync-demo/src/components/Counter.tsx +63 -0
- package/demo/react-broadcast-sync-demo/src/components/TextSync.tsx +61 -0
- package/demo/react-broadcast-sync-demo/src/components/TodoList.tsx +186 -0
- package/demo/react-broadcast-sync-demo/src/hooks/useCounterBroadcast.ts +94 -0
- package/demo/react-broadcast-sync-demo/src/hooks/useTextBroadcast.ts +37 -0
- package/demo/react-broadcast-sync-demo/src/hooks/useTodoBroadcast.ts +249 -0
- package/demo/react-broadcast-sync-demo/src/main.tsx +11 -0
- package/demo/react-broadcast-sync-demo/src/theme.ts +62 -0
- package/demo/react-broadcast-sync-demo/tsconfig.json +26 -0
- package/demo/react-broadcast-sync-demo/tsconfig.node.json +10 -0
- package/demo/react-broadcast-sync-demo/vite.config.ts +12 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/eslint.config.js +14 -5
- package/jest.config.cjs +8 -9
- package/package.json +63 -33
- package/rollup.config.mjs +27 -21
- package/src/BroadcastProvider.tsx +6 -6
- package/src/hooks/useBroadcastChannel.ts +122 -95
- package/src/index.ts +1 -1
- package/src/tests/BroadcastProvider.test.tsx +7 -7
- package/src/tests/debug.test.ts +65 -25
- package/src/tests/messageUtils.test.ts +20 -15
- package/src/tests/useBroadcastChannel.test.tsx +48 -33
- package/src/types/types.ts +19 -23
- package/src/utils/debug.ts +1 -1
- package/src/utils/messageUtils.ts +19 -24
- package/dist/useBroadcastChannel.d.ts +0 -3
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom hook for managing counter state synchronization across browser tabs.
|
|
3
|
+
*
|
|
4
|
+
* This hook provides functionality for:
|
|
5
|
+
* 1. Synchronizing a counter value across multiple browser tabs
|
|
6
|
+
* 2. Broadcasting hover state for visual feedback
|
|
7
|
+
* 3. Maintaining consistent state across all instances
|
|
8
|
+
*
|
|
9
|
+
* @returns {Object} Counter state and handlers
|
|
10
|
+
* @property {number} count - Current counter value
|
|
11
|
+
* @property {boolean} isHovered - Whether the counter is currently hovered
|
|
12
|
+
* @property {Function} increment - Function to increment the counter
|
|
13
|
+
* @property {Function} decrement - Function to decrement the counter
|
|
14
|
+
* @property {Function} handleMouseEnter - Function to handle mouse enter event
|
|
15
|
+
* @property {Function} handleMouseLeave - Function to handle mouse leave event
|
|
16
|
+
*/
|
|
17
|
+
import { useState, useEffect, useRef } from 'react';
|
|
18
|
+
import { useBroadcastChannel } from 'react-broadcast-sync';
|
|
19
|
+
|
|
20
|
+
export const useCounterBroadcast = () => {
|
|
21
|
+
const { messages, postMessage } = useBroadcastChannel('counter', {
|
|
22
|
+
keepLatestMessage: true,
|
|
23
|
+
namespace: 'demo-app',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const [count, setCount] = useState<number>(0);
|
|
27
|
+
const [isSyncing, setIsSyncing] = useState(false);
|
|
28
|
+
const [showCheckmark, setShowCheckmark] = useState(false);
|
|
29
|
+
|
|
30
|
+
const syncTimeoutRef = useRef<NodeJS.Timeout>();
|
|
31
|
+
const checkmarkTimeoutRef = useRef<NodeJS.Timeout>();
|
|
32
|
+
|
|
33
|
+
// Update count when new messages arrive
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const lastMessage = messages[messages.length - 1];
|
|
36
|
+
if (lastMessage?.message !== undefined) {
|
|
37
|
+
// Clear any existing timeouts
|
|
38
|
+
if (syncTimeoutRef.current) {
|
|
39
|
+
clearTimeout(syncTimeoutRef.current);
|
|
40
|
+
}
|
|
41
|
+
if (checkmarkTimeoutRef.current) {
|
|
42
|
+
clearTimeout(checkmarkTimeoutRef.current);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Reset states
|
|
46
|
+
setIsSyncing(true);
|
|
47
|
+
setShowCheckmark(false);
|
|
48
|
+
|
|
49
|
+
// Update count
|
|
50
|
+
setCount(lastMessage.message);
|
|
51
|
+
|
|
52
|
+
// Show checkmark after a short delay
|
|
53
|
+
syncTimeoutRef.current = setTimeout(() => {
|
|
54
|
+
setIsSyncing(false);
|
|
55
|
+
setShowCheckmark(true);
|
|
56
|
+
|
|
57
|
+
// Hide checkmark after showing it
|
|
58
|
+
checkmarkTimeoutRef.current = setTimeout(() => {
|
|
59
|
+
setShowCheckmark(false);
|
|
60
|
+
}, 1000);
|
|
61
|
+
}, 500);
|
|
62
|
+
|
|
63
|
+
// Cleanup
|
|
64
|
+
return () => {
|
|
65
|
+
if (syncTimeoutRef.current) {
|
|
66
|
+
clearTimeout(syncTimeoutRef.current);
|
|
67
|
+
}
|
|
68
|
+
if (checkmarkTimeoutRef.current) {
|
|
69
|
+
clearTimeout(checkmarkTimeoutRef.current);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}, [messages]);
|
|
74
|
+
|
|
75
|
+
const increment = () => {
|
|
76
|
+
const newCount = count + 1;
|
|
77
|
+
setCount(newCount);
|
|
78
|
+
postMessage('counter', newCount);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const decrement = () => {
|
|
82
|
+
const newCount = count - 1;
|
|
83
|
+
setCount(newCount);
|
|
84
|
+
postMessage('counter', newCount);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
count,
|
|
89
|
+
increment,
|
|
90
|
+
decrement,
|
|
91
|
+
isSyncing,
|
|
92
|
+
showCheckmark,
|
|
93
|
+
};
|
|
94
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { useBroadcastChannel } from 'react-broadcast-sync';
|
|
3
|
+
|
|
4
|
+
export const useTextBroadcast = () => {
|
|
5
|
+
const { messages, postMessage } = useBroadcastChannel('text', {
|
|
6
|
+
keepLatestMessage: true,
|
|
7
|
+
namespace: 'demo-app',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const lastMessage = messages[messages.length - 1];
|
|
11
|
+
const [localText, setLocalText] = useState<string>(lastMessage ? lastMessage.message : '');
|
|
12
|
+
|
|
13
|
+
// Update local state when new messages arrive
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (lastMessage) {
|
|
16
|
+
setLocalText(lastMessage.message);
|
|
17
|
+
}
|
|
18
|
+
}, [lastMessage]);
|
|
19
|
+
|
|
20
|
+
// Initialize text if no messages exist
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (messages.length === 0) {
|
|
23
|
+
postMessage('text', '');
|
|
24
|
+
}
|
|
25
|
+
}, []);
|
|
26
|
+
|
|
27
|
+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
28
|
+
const newText = event.target.value;
|
|
29
|
+
setLocalText(newText); // Update local state immediately
|
|
30
|
+
postMessage('text', newText); // Broadcast to other tabs
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
localText,
|
|
35
|
+
handleChange,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom hook for managing todo list synchronization across browser tabs.
|
|
3
|
+
*
|
|
4
|
+
* This hook provides functionality for:
|
|
5
|
+
* 1. Synchronizing todo items across multiple browser tabs
|
|
6
|
+
* 2. Broadcasting hover states for visual feedback
|
|
7
|
+
* 3. Synchronizing scroll position across tabs
|
|
8
|
+
* 4. Managing todo item completion status
|
|
9
|
+
*
|
|
10
|
+
* @returns {Object} Todo list state and handlers
|
|
11
|
+
* @property {Todo[]} todos - Array of todo items
|
|
12
|
+
* @property {string} newTodo - Current input value for new todo
|
|
13
|
+
* @property {Function} setNewTodo - Function to update new todo input
|
|
14
|
+
* @property {number|null} hoveredTodoId - ID of currently hovered todo
|
|
15
|
+
* @property {boolean} isScrolling - Whether the list is currently scrolling
|
|
16
|
+
* @property {boolean} isSyncing - Whether scroll sync is in progress
|
|
17
|
+
* @property {boolean} showCheckmark - Whether to show sync completion indicator
|
|
18
|
+
* @property {React.RefObject} scrollContainerRef - Ref for the scroll container
|
|
19
|
+
* @property {Function} handleScroll - Function to handle scroll events
|
|
20
|
+
* @property {Function} addTodo - Function to add a new todo
|
|
21
|
+
* @property {Function} toggleTodo - Function to toggle todo completion
|
|
22
|
+
* @property {Function} handleTodoMouseEnter - Function to handle todo hover
|
|
23
|
+
* @property {Function} handleTodoMouseLeave - Function to handle todo unhover
|
|
24
|
+
*/
|
|
25
|
+
import { useState, useEffect, useRef } from 'react';
|
|
26
|
+
import { useBroadcastChannel } from 'react-broadcast-sync';
|
|
27
|
+
|
|
28
|
+
interface Todo {
|
|
29
|
+
id: string;
|
|
30
|
+
text: string;
|
|
31
|
+
completed: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Hook for managing todo list synchronization across browser tabs.
|
|
36
|
+
* Provides real-time todo updates, completion status tracking, and scroll position sync.
|
|
37
|
+
*
|
|
38
|
+
* @returns {Object} Todo state and handlers
|
|
39
|
+
* @property {Todo[]} todos - List of todos
|
|
40
|
+
* @property {string} newTodo - New todo input value
|
|
41
|
+
* @property {Function} handleNewTodoChange - Function to handle new todo input changes
|
|
42
|
+
* @property {Function} handleAddTodo - Function to add a new todo
|
|
43
|
+
* @property {Function} handleToggleTodo - Function to toggle todo completion
|
|
44
|
+
* @property {Function} handleDeleteTodo - Function to delete a todo
|
|
45
|
+
* @property {Function} handleScroll - Function to handle scroll position sync
|
|
46
|
+
*/
|
|
47
|
+
export const useTodoBroadcast = () => {
|
|
48
|
+
const { messages, postMessage } = useBroadcastChannel('todos', {
|
|
49
|
+
keepLatestMessage: true,
|
|
50
|
+
namespace: 'demo-app',
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Add hover state broadcast channel
|
|
54
|
+
const { messages: hoverMessages, postMessage: postHoverMessage } = useBroadcastChannel(
|
|
55
|
+
'todos-hover',
|
|
56
|
+
{
|
|
57
|
+
keepLatestMessage: true,
|
|
58
|
+
namespace: 'demo-app',
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Add scroll position broadcast channel
|
|
63
|
+
const { messages: scrollMessages, postMessage: postScrollMessage } = useBroadcastChannel(
|
|
64
|
+
'todos-scroll',
|
|
65
|
+
{
|
|
66
|
+
keepLatestMessage: true,
|
|
67
|
+
namespace: 'demo-app',
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const lastMessage = messages[messages.length - 1];
|
|
72
|
+
const lastHoverMessage = hoverMessages[hoverMessages.length - 1];
|
|
73
|
+
const lastScrollMessage = scrollMessages[scrollMessages.length - 1];
|
|
74
|
+
|
|
75
|
+
const [todos, setTodos] = useState<Todo[]>(lastMessage ? lastMessage.message : []);
|
|
76
|
+
const [newTodo, setNewTodo] = useState('');
|
|
77
|
+
const [hoveredTodoId, setHoveredTodoId] = useState<string | null>(
|
|
78
|
+
lastHoverMessage ? lastHoverMessage.message : null
|
|
79
|
+
);
|
|
80
|
+
const [isScrolling, setIsScrolling] = useState(false);
|
|
81
|
+
const [isSyncing, setIsSyncing] = useState(false);
|
|
82
|
+
const [showCheckmark, setShowCheckmark] = useState(false);
|
|
83
|
+
|
|
84
|
+
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
85
|
+
const scrollTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
|
86
|
+
const syncTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
|
87
|
+
const checkmarkTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
|
88
|
+
|
|
89
|
+
// Update local state when new messages arrive
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (lastMessage) {
|
|
92
|
+
setTodos(lastMessage.message);
|
|
93
|
+
}
|
|
94
|
+
}, [lastMessage]);
|
|
95
|
+
|
|
96
|
+
// Update hover state when new hover messages arrive
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (lastHoverMessage) {
|
|
99
|
+
setHoveredTodoId(lastHoverMessage.message);
|
|
100
|
+
}
|
|
101
|
+
}, [lastHoverMessage]);
|
|
102
|
+
|
|
103
|
+
// Update scroll position when new scroll messages arrive
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (lastScrollMessage && scrollContainerRef.current) {
|
|
106
|
+
// Clear any existing timeouts
|
|
107
|
+
if (syncTimeoutRef.current) {
|
|
108
|
+
clearTimeout(syncTimeoutRef.current);
|
|
109
|
+
}
|
|
110
|
+
if (checkmarkTimeoutRef.current) {
|
|
111
|
+
clearTimeout(checkmarkTimeoutRef.current);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Reset states
|
|
115
|
+
setIsSyncing(true);
|
|
116
|
+
setShowCheckmark(false);
|
|
117
|
+
setIsScrolling(false);
|
|
118
|
+
|
|
119
|
+
// Perform scroll
|
|
120
|
+
scrollContainerRef.current.scrollTo({
|
|
121
|
+
top: lastScrollMessage.message,
|
|
122
|
+
behavior: 'smooth',
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Listen for scroll end
|
|
126
|
+
const handleScrollEnd = () => {
|
|
127
|
+
setIsSyncing(false);
|
|
128
|
+
setShowCheckmark(true);
|
|
129
|
+
|
|
130
|
+
// Hide indicator after showing checkmark
|
|
131
|
+
checkmarkTimeoutRef.current = setTimeout(() => {
|
|
132
|
+
setShowCheckmark(false);
|
|
133
|
+
}, 1000);
|
|
134
|
+
|
|
135
|
+
// Remove the event listener
|
|
136
|
+
scrollContainerRef.current?.removeEventListener('scrollend', handleScrollEnd);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Add scroll end listener
|
|
140
|
+
scrollContainerRef.current.addEventListener('scrollend', handleScrollEnd);
|
|
141
|
+
|
|
142
|
+
// Fallback timeout in case scrollend event doesn't fire
|
|
143
|
+
syncTimeoutRef.current = setTimeout(() => {
|
|
144
|
+
handleScrollEnd();
|
|
145
|
+
}, 500);
|
|
146
|
+
|
|
147
|
+
// Cleanup
|
|
148
|
+
return () => {
|
|
149
|
+
scrollContainerRef.current?.removeEventListener('scrollend', handleScrollEnd);
|
|
150
|
+
if (syncTimeoutRef.current) {
|
|
151
|
+
clearTimeout(syncTimeoutRef.current);
|
|
152
|
+
}
|
|
153
|
+
if (checkmarkTimeoutRef.current) {
|
|
154
|
+
clearTimeout(checkmarkTimeoutRef.current);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}, [lastScrollMessage]);
|
|
159
|
+
|
|
160
|
+
// Initialize todos if no messages exist
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
if (messages.length === 0) {
|
|
163
|
+
postMessage('todos', []);
|
|
164
|
+
}
|
|
165
|
+
}, []);
|
|
166
|
+
|
|
167
|
+
const handleScrollSync = (e: React.UIEvent<HTMLDivElement>) => {
|
|
168
|
+
const newScrollPosition = e.currentTarget.scrollTop;
|
|
169
|
+
|
|
170
|
+
// Clear any existing timeouts
|
|
171
|
+
if (scrollTimeoutRef.current) {
|
|
172
|
+
clearTimeout(scrollTimeoutRef.current);
|
|
173
|
+
}
|
|
174
|
+
if (syncTimeoutRef.current) {
|
|
175
|
+
clearTimeout(syncTimeoutRef.current);
|
|
176
|
+
}
|
|
177
|
+
if (checkmarkTimeoutRef.current) {
|
|
178
|
+
clearTimeout(checkmarkTimeoutRef.current);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Reset states
|
|
182
|
+
setIsScrolling(true);
|
|
183
|
+
setIsSyncing(true);
|
|
184
|
+
setShowCheckmark(false);
|
|
185
|
+
|
|
186
|
+
// Debounce the scroll message to avoid too many updates
|
|
187
|
+
scrollTimeoutRef.current = setTimeout(() => {
|
|
188
|
+
postScrollMessage('todos-scroll', newScrollPosition);
|
|
189
|
+
}, 100);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const handleNewTodoChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
193
|
+
setNewTodo(event.target.value);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const handleAddTodo = (event: React.FormEvent) => {
|
|
197
|
+
event.preventDefault();
|
|
198
|
+
if (newTodo.trim()) {
|
|
199
|
+
const updated = [
|
|
200
|
+
...todos,
|
|
201
|
+
{ id: Date.now().toString(), text: newTodo.trim(), completed: false },
|
|
202
|
+
];
|
|
203
|
+
setTodos(updated);
|
|
204
|
+
postMessage('todos', updated);
|
|
205
|
+
setNewTodo('');
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const handleToggleTodo = (id: string) => {
|
|
210
|
+
const updated = todos.map(todo =>
|
|
211
|
+
todo.id === id ? { ...todo, completed: !todo.completed } : todo
|
|
212
|
+
);
|
|
213
|
+
setTodos(updated);
|
|
214
|
+
postMessage('todos', updated);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const handleDeleteTodo = (id: string) => {
|
|
218
|
+
const updated = todos.filter(todo => todo.id !== id);
|
|
219
|
+
setTodos(updated);
|
|
220
|
+
postMessage('todos', updated);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const handleTodoMouseEnter = (id: string) => {
|
|
224
|
+
setHoveredTodoId(id);
|
|
225
|
+
postHoverMessage('todos-hover', id);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const handleTodoMouseLeave = () => {
|
|
229
|
+
setHoveredTodoId(null);
|
|
230
|
+
postHoverMessage('todos-hover', null);
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
todos,
|
|
235
|
+
newTodo,
|
|
236
|
+
handleNewTodoChange,
|
|
237
|
+
handleAddTodo,
|
|
238
|
+
handleToggleTodo,
|
|
239
|
+
handleDeleteTodo,
|
|
240
|
+
handleScrollSync,
|
|
241
|
+
hoveredTodoId,
|
|
242
|
+
isScrolling,
|
|
243
|
+
isSyncing,
|
|
244
|
+
showCheckmark,
|
|
245
|
+
scrollContainerRef,
|
|
246
|
+
handleTodoMouseEnter,
|
|
247
|
+
handleTodoMouseLeave,
|
|
248
|
+
};
|
|
249
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom/client';
|
|
3
|
+
import App from './App';
|
|
4
|
+
import { CssBaseline } from '@mui/material';
|
|
5
|
+
|
|
6
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
7
|
+
<React.StrictMode>
|
|
8
|
+
<CssBaseline />
|
|
9
|
+
<App />
|
|
10
|
+
</React.StrictMode>
|
|
11
|
+
);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createTheme } from '@mui/material/styles';
|
|
2
|
+
|
|
3
|
+
export const theme = createTheme({
|
|
4
|
+
palette: {
|
|
5
|
+
primary: {
|
|
6
|
+
main: '#5EC13D',
|
|
7
|
+
dark: '#3FAD3A',
|
|
8
|
+
contrastText: '#fff',
|
|
9
|
+
},
|
|
10
|
+
secondary: {
|
|
11
|
+
main: '#3FAD3A',
|
|
12
|
+
contrastText: '#fff',
|
|
13
|
+
},
|
|
14
|
+
background: {
|
|
15
|
+
default: '#0D1117',
|
|
16
|
+
paper: '#161B22',
|
|
17
|
+
},
|
|
18
|
+
text: {
|
|
19
|
+
primary: '#fff',
|
|
20
|
+
secondary: '#bdbdbd',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
typography: {
|
|
24
|
+
fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
|
|
25
|
+
h1: {
|
|
26
|
+
fontWeight: 700,
|
|
27
|
+
},
|
|
28
|
+
h2: {
|
|
29
|
+
fontWeight: 600,
|
|
30
|
+
},
|
|
31
|
+
h3: {
|
|
32
|
+
fontWeight: 600,
|
|
33
|
+
},
|
|
34
|
+
h4: {
|
|
35
|
+
fontWeight: 600,
|
|
36
|
+
},
|
|
37
|
+
h5: {
|
|
38
|
+
fontWeight: 600,
|
|
39
|
+
},
|
|
40
|
+
h6: {
|
|
41
|
+
fontWeight: 600,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
components: {
|
|
45
|
+
MuiButton: {
|
|
46
|
+
styleOverrides: {
|
|
47
|
+
root: {
|
|
48
|
+
textTransform: 'none',
|
|
49
|
+
borderRadius: 8,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
MuiPaper: {
|
|
54
|
+
styleOverrides: {
|
|
55
|
+
root: {
|
|
56
|
+
borderRadius: 12,
|
|
57
|
+
background: '#161B22',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"types": ["node"],
|
|
6
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"isolatedModules": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"jsx": "react-jsx",
|
|
17
|
+
|
|
18
|
+
/* Linting */
|
|
19
|
+
"strict": true,
|
|
20
|
+
"noUnusedLocals": true,
|
|
21
|
+
"noUnusedParameters": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true
|
|
23
|
+
},
|
|
24
|
+
"include": ["src"],
|
|
25
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
26
|
+
}
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("react"),r=require("react/jsx-runtime");const t="__INTERNAL__",s=()=>Math.random().toString(36).substr(2,9),n=(e,r)=>{const t=`${s()}-${e}-${r}`;return btoa(t).replace(/=+$/,"")},a=e=>!!e.expirationDate&&e.expirationDate<Date.now(),o=(e,r,s="")=>{const n=btoa(`react-broadcast-sync:${e}:${`${r}-${s}`}`);return`${t}:${e}:${n}`},c=e=>e.startsWith(`${t}:`),
|
|
1
|
+
"use strict";var e=require("react"),r=require("react/jsx-runtime");const t="__INTERNAL__",s=()=>Math.random().toString(36).substr(2,9),n=(e,r)=>{const t=`${s()}-${e}-${r}`;return btoa(t).replace(/=+$/,"")},a=e=>!!e.expirationDate&&e.expirationDate<Date.now(),o=(e,r,s="")=>{const n=btoa(`react-broadcast-sync:${e}:${`${r}-${s}`}`);return`${t}:${e}:${n}`},c=e=>e.startsWith(`${t}:`),i=(e,r,t)=>{if("undefined"==typeof process||!process.env||"true"!==process.env.REACT_APP_DEBUG_BROADCAST)return;const s=`[${(new Date).toISOString()}] [react-broadcast-sync]`;switch(e){case"info":console.log(s,r,t||"");break;case"warn":console.warn(s,r,t||"");break;case"error":console.error(s,r,t||"")}},l={created:e=>i("info","Channel created:",e),closed:e=>i("info","Channel closed:",e)},u={sent:e=>i("info","Message sent:",e),received:e=>i("info","Message received:",e),cleared:e=>i("info","Message cleared:",e),expired:e=>i("info","Message expired:",e),duplicate:e=>i("warn","Duplicate message ignored:",e)},d={started:()=>i("info","Cleanup started"),completed:e=>i("info","Cleanup completed:",{removedCount:e})},p=e=>i("error","Error:",e),f="CLEAR_MESSAGE",E="CLEAR_ALL_MESSAGES",v=(r,t={})=>{const{sourceName:i,cleaningInterval:v=1e3,keepLatestMessage:S=!1,registeredTypes:h=[],namespace:C="",deduplicationTTL:g=3e5,cleanupDebounceMs:A=0}=t,[m,M]=e.useState([]),[L,b]=e.useState([]),[_,y]=e.useState(null),R=e.useRef(null),w=e.useRef(new Map),x=e.useRef(i||`tab-${s()}`).current,D=e.useRef({CLEAR_MESSAGE:o(f,r,C),CLEAR_ALL_MESSAGES:o(E,r,C)}).current,B=e.useMemo((()=>`${r}-${C}`),[r,C]),$=e.useCallback((()=>{M((e=>e.filter((e=>!a(e)))))}),[]),G=e.useRef(A>0?((e,r)=>{let t,s=null,n=null;const a=(...a)=>{n=a,null!==s&&clearTimeout(s),s=setTimeout((()=>{s=null,null!==n&&(t=e(...n),n=null)}),r)};return a.cancel=()=>{null!==s&&(clearTimeout(s),s=null,n=null)},a.flush=()=>(null!==s&&(clearTimeout(s),s=null,null!==n&&(t=e(...n),n=null)),t),a})($,A):Object.assign($,{cancel:()=>{}})).current,k=e.useCallback((e=>{y(e),setTimeout((()=>y(null)),3e3)}),[]),T=e.useCallback((e=>{var r;M((r=>r.filter((r=>r.id!==e)))),b((r=>r.filter((r=>r.id!==e)))),null===(r=R.current)||void 0===r||r.postMessage({id:e,type:D.CLEAR_MESSAGE,source:x}),u.cleared(e)}),[x,D.CLEAR_MESSAGE]),P=e.useCallback((()=>{var e;M([]),b([]),null===(e=R.current)||void 0===e||e.postMessage({id:D.CLEAR_ALL_MESSAGES,type:D.CLEAR_ALL_MESSAGES,source:x})}),[x,D.CLEAR_ALL_MESSAGES]),j=e.useCallback((e=>{var r;b((r=>r.filter((r=>r.id!==e)))),null===(r=R.current)||void 0===r||r.postMessage({id:e,type:D.CLEAR_MESSAGE,source:x})}),[x,D.CLEAR_MESSAGE]),I=e.useCallback(((e,r,t={})=>{const s=R.current;if(!s){const e="Channel not available";return p(e),void k(e)}try{const a=((e,r,t,s={})=>{var a;const o=Date.now();return{id:n(t,o),type:e,message:r,timestamp:o,source:t,expirationDate:null!==(a=s.expirationDate)&&void 0!==a?a:s.expirationDuration?o+s.expirationDuration:void 0}})(e,r,x,t);s.postMessage(a),u.sent(a),b((e=>[...e,a]))}catch(e){const r="Failed to send message";p(r),k(r)}}),[x,k]),N=e.useCallback((e=>{try{const r=e.data;if(u.received(r),!(e=>Boolean(e&&"object"==typeof e&&"id"in e))(r))return;if(r.source===x)return;if(!c(r.type)&&h.length>0&&!h.includes(r.type))return;if(a(r))return void u.expired(r.id);if((e=>e&&"object"==typeof e&&"string"==typeof e.id&&"string"==typeof e.source&&"string"==typeof e.type&&c(e.type))(r)){if(r.type===D.CLEAR_MESSAGE)return void M((e=>e.filter((e=>!(e.id===r.id&&e.source===r.source)))));if(r.type===D.CLEAR_ALL_MESSAGES)return void M((e=>e.filter((e=>e.source!==r.source))))}const t=Date.now(),s=w.current.get(r.id);if(s&&t-s<g)return void u.duplicate(r.id);w.current.set(r.id,t),M((e=>S?[r]:[...e,r]))}catch(e){const r="Error processing broadcast message";p(r),k(r)}}),[x,h,S,k,D,g]);return e.useEffect((()=>{if("undefined"==typeof BroadcastChannel){const e="BroadcastChannel is not supported in this browser. Please check browser compatibility.";return p(e),void k(e)}const e=new BroadcastChannel(B);return R.current=e,l.created(B),e.addEventListener("message",N),()=>{e.removeEventListener("message",N),e.close(),l.closed(B)}}),[B,N,k]),e.useEffect((()=>{if(v<=0)return;const e=setInterval((()=>{d.started();const e=m.length;G(),d.completed(e-m.length)}),v);return()=>{var r;clearInterval(e),A>0&&(null===(r=G.cancel)||void 0===r||r.call(G))}}),[v,G,A]),e.useEffect((()=>{const e=setInterval((()=>{const e=Date.now();for(const[r,t]of w.current.entries())e-t>=g&&w.current.delete(r)}),6e4);return()=>clearInterval(e)}),[g]),{channelName:B,messages:m,sentMessages:L,postMessage:I,clearMessage:T,clearAllMessages:P,clearSentMessage:j,error:_}},S=e.createContext(void 0);class h extends e.Component{constructor(){super(...arguments),this.state={hasError:!1}}static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(e){console.error("BroadcastChannel error:",e)}render(){return this.state.hasError?r.jsx("div",{children:"Error occurred in BroadcastProvider"}):this.props.children}}exports.BroadcastProvider=({children:t,channelName:s})=>{const{messages:n,error:a}=v(s),[o,c]=e.useState(null),i=e.useRef(null);e.useEffect((()=>{if("undefined"==typeof BroadcastChannel)return void c("BroadcastChannel is not supported in this browser. Please check browser compatibility.");const e=new BroadcastChannel(s);return i.current=e,()=>{e.close()}}),[s]);const l=e.useMemo((()=>({messages:n,error:o||a,channel:i.current})),[n,o,a]);return r.jsx(h,{children:r.jsx(S.Provider,{value:l,children:t})})},exports.useBroadcastChannel=v,exports.useBroadcastProvider=()=>{const r=e.useContext(S);if(!r)throw new Error("useBroadcastProvider must be used within a BroadcastProvider");return r};
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/utils/messageUtils.ts","../src/utils/debug.ts","../src/hooks/useBroadcastChannel.ts","../src/BroadcastProvider.tsx"],"sourcesContent":[null,null,null,null],"names":["INTERNAL_PREFIX","generateRandomPart","Math","random","toString","substr","generateMessageId","source","timestamp","raw","btoa","replace","isMessageExpired","message","expirationDate","Date","now","getInternalMessageType","baseType","channelName","namespace","hash","isInternalType","type","startsWith","
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/utils/messageUtils.ts","../src/utils/debug.ts","../src/hooks/useBroadcastChannel.ts","../src/BroadcastProvider.tsx"],"sourcesContent":[null,null,null,null],"names":["INTERNAL_PREFIX","generateRandomPart","Math","random","toString","substr","generateMessageId","source","timestamp","raw","btoa","replace","isMessageExpired","message","expirationDate","Date","now","getInternalMessageType","baseType","channelName","namespace","hash","isInternalType","type","startsWith","log","level","data","process","env","REACT_APP_DEBUG_BROADCAST","prefix","toISOString","console","warn","error","debug","created","closed","sent","received","cleared","messageId","expired","duplicate","started","completed","removedCount","INTERNAL_MESSAGE_TYPES","useBroadcastChannel","options","sourceName","cleaningInterval","keepLatestMessage","registeredTypes","deduplicationTTL","cleanupDebounceMs","messages","setMessages","useState","sentMessages","setSentMessages","setError","channel","useRef","receivedMessageIds","Map","current","internalTypes","CLEAR_MESSAGE","CLEAR_ALL_MESSAGES","resolvedChannelName","useMemo","performCleanup","useCallback","prev","filter","msg","debouncedCleanup","debounce","fn","wait","result","timeoutId","lastArgs","debounced","args","clearTimeout","setTimeout","cancel","flush","Object","assign","setErrorMessage","clearMessage","id","_a","postMessage","clearAllMessages","clearSentMessage","messageType","messageContent","channelCurrent","createMessage","content","expirationDuration","undefined","e","handleMessage","event","Boolean","isValidMessage","length","includes","isValidInternalClearMessage","receivedAt","get","set","useEffect","BroadcastChannel","bc","addEventListener","removeEventListener","close","interval","setInterval","before","clearInterval","call","entries","delete","BroadcastChannelContext","createContext","ErrorBoundary","React","Component","constructor","this","state","hasError","getDerivedStateFromError","componentDidCatch","render","_jsx","props","children","BroadcastProvider","hookError","value","Provider","useBroadcastProvider","context","useContext","Error"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { useBroadcastChannel } from './hooks/useBroadcastChannel';
|
|
2
2
|
export { BroadcastProvider, useBroadcastProvider } from './BroadcastProvider';
|
|
3
|
-
export type { BroadcastOptions, SendMessageOptions, BroadcastMessage, BroadcastActions } from './types/types';
|
|
3
|
+
export type { BroadcastOptions, SendMessageOptions, BroadcastMessage, BroadcastActions, } from './types/types';
|
package/dist/index.esm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e,{useState as r,useRef as t,useMemo as n,useCallback as s,useEffect as o,createContext as a,useContext as c}from"react";import{jsx as i}from"react/jsx-runtime";const l="__INTERNAL__",u=()=>Math.random().toString(36).substr(2,9),d=(e,r)=>{const t=`${u()}-${e}-${r}`;return btoa(t).replace(/=+$/,"")},p=e=>!!e.expirationDate&&e.expirationDate<Date.now(),E=(e,r,t="")=>{const n=btoa(`react-broadcast-sync:${e}:${`${r}-${t}`}`);return`${l}:${e}:${n}`},f=e=>e.startsWith(`${l}:`),
|
|
1
|
+
import e,{useState as r,useRef as t,useMemo as n,useCallback as s,useEffect as o,createContext as a,useContext as c}from"react";import{jsx as i}from"react/jsx-runtime";const l="__INTERNAL__",u=()=>Math.random().toString(36).substr(2,9),d=(e,r)=>{const t=`${u()}-${e}-${r}`;return btoa(t).replace(/=+$/,"")},p=e=>!!e.expirationDate&&e.expirationDate<Date.now(),E=(e,r,t="")=>{const n=btoa(`react-broadcast-sync:${e}:${`${r}-${t}`}`);return`${l}:${e}:${n}`},f=e=>e.startsWith(`${l}:`),v=(e,r,t)=>{if("undefined"==typeof process||!process.env||"true"!==process.env.REACT_APP_DEBUG_BROADCAST)return;const n=`[${(new Date).toISOString()}] [react-broadcast-sync]`;switch(e){case"info":console.log(n,r,t||"");break;case"warn":console.warn(n,r,t||"");break;case"error":console.error(n,r,t||"")}},h={created:e=>v("info","Channel created:",e),closed:e=>v("info","Channel closed:",e)},g={sent:e=>v("info","Message sent:",e),received:e=>v("info","Message received:",e),cleared:e=>v("info","Message cleared:",e),expired:e=>v("info","Message expired:",e),duplicate:e=>v("warn","Duplicate message ignored:",e)},S={started:()=>v("info","Cleanup started"),completed:e=>v("info","Cleanup completed:",{removedCount:e})},A=e=>v("error","Error:",e),m="CLEAR_MESSAGE",C="CLEAR_ALL_MESSAGES",L=(e,a={})=>{const{sourceName:c,cleaningInterval:i=1e3,keepLatestMessage:l=!1,registeredTypes:v=[],namespace:L="",deduplicationTTL:M=3e5,cleanupDebounceMs:_=0}=a,[y,b]=r([]),[w,D]=r([]),[R,$]=r(null),G=t(null),B=t(new Map),x=t(c||`tab-${u()}`).current,T=t({CLEAR_MESSAGE:E(m,e,L),CLEAR_ALL_MESSAGES:E(C,e,L)}).current,P=n((()=>`${e}-${L}`),[e,L]),I=s((()=>{b((e=>e.filter((e=>!p(e)))))}),[]),k=t(_>0?((e,r)=>{let t,n=null,s=null;const o=(...o)=>{s=o,null!==n&&clearTimeout(n),n=setTimeout((()=>{n=null,null!==s&&(t=e(...s),s=null)}),r)};return o.cancel=()=>{null!==n&&(clearTimeout(n),n=null,s=null)},o.flush=()=>(null!==n&&(clearTimeout(n),n=null,null!==s&&(t=e(...s),s=null)),t),o})(I,_):Object.assign(I,{cancel:()=>{}})).current,N=s((e=>{$(e),setTimeout((()=>$(null)),3e3)}),[]),j=s((e=>{var r;b((r=>r.filter((r=>r.id!==e)))),D((r=>r.filter((r=>r.id!==e)))),null===(r=G.current)||void 0===r||r.postMessage({id:e,type:T.CLEAR_MESSAGE,source:x}),g.cleared(e)}),[x,T.CLEAR_MESSAGE]),O=s((()=>{var e;b([]),D([]),null===(e=G.current)||void 0===e||e.postMessage({id:T.CLEAR_ALL_MESSAGES,type:T.CLEAR_ALL_MESSAGES,source:x})}),[x,T.CLEAR_ALL_MESSAGES]),F=s((e=>{var r;D((r=>r.filter((r=>r.id!==e)))),null===(r=G.current)||void 0===r||r.postMessage({id:e,type:T.CLEAR_MESSAGE,source:x})}),[x,T.CLEAR_MESSAGE]),U=s(((e,r,t={})=>{const n=G.current;if(!n){const e="Channel not available";return A(e),void N(e)}try{const s=((e,r,t,n={})=>{var s;const o=Date.now();return{id:d(t,o),type:e,message:r,timestamp:o,source:t,expirationDate:null!==(s=n.expirationDate)&&void 0!==s?s:n.expirationDuration?o+n.expirationDuration:void 0}})(e,r,x,t);n.postMessage(s),g.sent(s),D((e=>[...e,s]))}catch(e){const r="Failed to send message";A(r),N(r)}}),[x,N]),W=s((e=>{try{const r=e.data;if(g.received(r),!(e=>Boolean(e&&"object"==typeof e&&"id"in e))(r))return;if(r.source===x)return;if(!f(r.type)&&v.length>0&&!v.includes(r.type))return;if(p(r))return void g.expired(r.id);if((e=>e&&"object"==typeof e&&"string"==typeof e.id&&"string"==typeof e.source&&"string"==typeof e.type&&f(e.type))(r)){if(r.type===T.CLEAR_MESSAGE)return void b((e=>e.filter((e=>!(e.id===r.id&&e.source===r.source)))));if(r.type===T.CLEAR_ALL_MESSAGES)return void b((e=>e.filter((e=>e.source!==r.source))))}const t=Date.now(),n=B.current.get(r.id);if(n&&t-n<M)return void g.duplicate(r.id);B.current.set(r.id,t),b((e=>l?[r]:[...e,r]))}catch(e){const r="Error processing broadcast message";A(r),N(r)}}),[x,v,l,N,T,M]);return o((()=>{if("undefined"==typeof BroadcastChannel){const e="BroadcastChannel is not supported in this browser. Please check browser compatibility.";return A(e),void N(e)}const e=new BroadcastChannel(P);return G.current=e,h.created(P),e.addEventListener("message",W),()=>{e.removeEventListener("message",W),e.close(),h.closed(P)}}),[P,W,N]),o((()=>{if(i<=0)return;const e=setInterval((()=>{S.started();const e=y.length;k(),S.completed(e-y.length)}),i);return()=>{var r;clearInterval(e),_>0&&(null===(r=k.cancel)||void 0===r||r.call(k))}}),[i,k,_]),o((()=>{const e=setInterval((()=>{const e=Date.now();for(const[r,t]of B.current.entries())e-t>=M&&B.current.delete(r)}),6e4);return()=>clearInterval(e)}),[M]),{channelName:P,messages:y,sentMessages:w,postMessage:U,clearMessage:j,clearAllMessages:O,clearSentMessage:F,error:R}},M=a(void 0);class _ extends e.Component{constructor(){super(...arguments),this.state={hasError:!1}}static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(e){console.error("BroadcastChannel error:",e)}render(){return this.state.hasError?i("div",{children:"Error occurred in BroadcastProvider"}):this.props.children}}const y=({children:r,channelName:s})=>{const{messages:a,error:c}=L(s),[l,u]=e.useState(null),d=t(null);o((()=>{if("undefined"==typeof BroadcastChannel)return void u("BroadcastChannel is not supported in this browser. Please check browser compatibility.");const e=new BroadcastChannel(s);return d.current=e,()=>{e.close()}}),[s]);const p=n((()=>({messages:a,error:l||c,channel:d.current})),[a,l,c]);return i(_,{children:i(M.Provider,{value:p,children:r})})},b=()=>{const e=c(M);if(!e)throw new Error("useBroadcastProvider must be used within a BroadcastProvider");return e};export{y as BroadcastProvider,L as useBroadcastChannel,b as useBroadcastProvider};
|
|
2
2
|
//# sourceMappingURL=index.esm.js.map
|