react-broadcast-sync 1.5.1 → 1.8.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.
Files changed (54) hide show
  1. package/README.md +59 -11
  2. package/dist/BroadcastProvider.d.ts +2 -7
  3. package/dist/index.cjs.js +1 -1
  4. package/dist/index.cjs.js.map +1 -1
  5. package/dist/index.esm.js +1 -1
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/types/types.d.ts +17 -0
  8. package/dist/utils/debug.d.ts +7 -1
  9. package/package.json +6 -1
  10. package/.eslintignore +0 -5
  11. package/.github/workflows/ci.yml +0 -61
  12. package/.github/workflows/release.yml +0 -39
  13. package/.husky/pre-commit +0 -5
  14. package/.nvmrc +0 -1
  15. package/.prettierignore +0 -4
  16. package/.prettierrc +0 -9
  17. package/.releaserc.json +0 -22
  18. package/CHANGELOG.md +0 -90
  19. package/assets/react-broadcast-sync-logo.png +0 -0
  20. package/commitlint.config.js +0 -3
  21. package/demo/react-broadcast-sync-demo/README.md +0 -130
  22. package/demo/react-broadcast-sync-demo/index.html +0 -14
  23. package/demo/react-broadcast-sync-demo/package-lock.json +0 -6379
  24. package/demo/react-broadcast-sync-demo/package.json +0 -42
  25. package/demo/react-broadcast-sync-demo/public/assets/react-broadcast-sync-demo-video.gif +0 -0
  26. package/demo/react-broadcast-sync-demo/public/assets/react-broadcast-sync-logo.png +0 -0
  27. package/demo/react-broadcast-sync-demo/src/App.tsx +0 -277
  28. package/demo/react-broadcast-sync-demo/src/components/Counter.tsx +0 -63
  29. package/demo/react-broadcast-sync-demo/src/components/TextSync.tsx +0 -61
  30. package/demo/react-broadcast-sync-demo/src/components/TodoList.tsx +0 -186
  31. package/demo/react-broadcast-sync-demo/src/hooks/useCounterBroadcast.ts +0 -96
  32. package/demo/react-broadcast-sync-demo/src/hooks/useTextBroadcast.ts +0 -37
  33. package/demo/react-broadcast-sync-demo/src/hooks/useTodoBroadcast.ts +0 -249
  34. package/demo/react-broadcast-sync-demo/src/main.tsx +0 -11
  35. package/demo/react-broadcast-sync-demo/src/theme.ts +0 -62
  36. package/demo/react-broadcast-sync-demo/tsconfig.json +0 -26
  37. package/demo/react-broadcast-sync-demo/tsconfig.node.json +0 -10
  38. package/demo/react-broadcast-sync-demo/vite.config.ts +0 -12
  39. package/eslint.config.js +0 -38
  40. package/jest.config.cjs +0 -9
  41. package/jest.setup.ts +0 -1
  42. package/rollup.config.mjs +0 -98
  43. package/src/BroadcastProvider.tsx +0 -80
  44. package/src/hooks/useBroadcastChannel.ts +0 -464
  45. package/src/index.ts +0 -9
  46. package/src/tests/BroadcastProvider.test.tsx +0 -163
  47. package/src/tests/debug.test.ts +0 -239
  48. package/src/tests/messageUtils.test.ts +0 -252
  49. package/src/tests/useBroadcastChannel.same-tab.test.tsx +0 -992
  50. package/src/tests/useBroadcastChannel.test.tsx +0 -1243
  51. package/src/types/types.ts +0 -124
  52. package/src/utils/debug.ts +0 -54
  53. package/src/utils/messageUtils.ts +0 -122
  54. package/tsconfig.json +0 -36
@@ -1,42 +0,0 @@
1
- {
2
- "name": "react-broadcast-sync-demo",
3
- "private": true,
4
- "version": "0.1.0",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "tsc && vite build",
9
- "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10
- "preview": "vite preview",
11
- "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,scss,md}\"",
12
- "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,scss,md}\""
13
- },
14
- "files": [
15
- "dist",
16
- "README.md"
17
- ],
18
- "dependencies": {
19
- "@emotion/react": "^11.11.3",
20
- "@emotion/styled": "^11.11.0",
21
- "@mui/icons-material": "^5.15.10",
22
- "@mui/material": "^5.15.10",
23
- "framer-motion": "^11.0.5",
24
- "react": "^18.2.0",
25
- "react-broadcast-sync": "^1.1.3",
26
- "react-dom": "^18.2.0"
27
- },
28
- "devDependencies": {
29
- "@types/node": "^22.15.29",
30
- "@types/react": "^18.2.55",
31
- "@types/react-dom": "^18.2.19",
32
- "@typescript-eslint/eslint-plugin": "^6.21.0",
33
- "@typescript-eslint/parser": "^6.21.0",
34
- "@vitejs/plugin-react": "^4.2.1",
35
- "eslint": "^8.56.0",
36
- "eslint-plugin-react-hooks": "^4.6.0",
37
- "eslint-plugin-react-refresh": "^0.4.5",
38
- "prettier": "^3.5.3",
39
- "typescript": "^5.2.2",
40
- "vite": "^5.1.0"
41
- }
42
- }
@@ -1,277 +0,0 @@
1
- import { ThemeProvider } from '@mui/material/styles';
2
- import {
3
- CssBaseline,
4
- Container,
5
- Box,
6
- Typography,
7
- Grid,
8
- Link,
9
- IconButton,
10
- useMediaQuery,
11
- } from '@mui/material';
12
- import { motion } from 'framer-motion';
13
- import GitHubIcon from '@mui/icons-material/GitHub';
14
- import LinkedInIcon from '@mui/icons-material/LinkedIn';
15
- import CodeIcon from '@mui/icons-material/Code';
16
- import OpenInNewIcon from '@mui/icons-material/OpenInNew';
17
- import { theme } from './theme';
18
- import { Counter } from './components/Counter';
19
- import { TextSync } from './components/TextSync';
20
- import { TodoList } from './components/TodoList';
21
-
22
- const App = () => {
23
- const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
24
-
25
- return (
26
- <ThemeProvider theme={theme}>
27
- <CssBaseline />
28
- <Box
29
- sx={{
30
- minHeight: '100vh',
31
- bgcolor: 'background.default',
32
- display: 'flex',
33
- flexDirection: 'column',
34
- overflow: 'hidden',
35
- position: 'relative',
36
- '&::before': {
37
- content: '""',
38
- position: 'absolute',
39
- top: 0,
40
- left: 0,
41
- right: 0,
42
- height: '40vh',
43
- background: `linear-gradient(180deg, ${theme.palette.primary.main}15 0%, ${theme.palette.background.default} 100%)`,
44
- zIndex: 0,
45
- },
46
- }}
47
- >
48
- <Container maxWidth="lg" sx={{ flex: 1, py: 2, position: 'relative', zIndex: 1 }}>
49
- <motion.div
50
- initial={{ opacity: 0 }}
51
- animate={{ opacity: 1 }}
52
- transition={{ duration: 0.5 }}
53
- >
54
- <Box
55
- sx={{
56
- display: 'flex',
57
- flexDirection: 'column',
58
- alignItems: 'center',
59
- mb: { xs: 2, md: 3 },
60
- }}
61
- >
62
- <motion.img
63
- src="/assets/react-broadcast-sync-logo.png"
64
- alt="React Broadcast Sync Logo"
65
- style={{
66
- width: isMobile ? '60px' : '130px',
67
- height: 'auto',
68
- marginBottom: '12px',
69
- }}
70
- initial={{ scale: 0, opacity: 0, y: -100 }}
71
- animate={{ scale: 1, opacity: 1, y: 0 }}
72
- transition={{
73
- duration: 0.6,
74
- type: 'spring',
75
- stiffness: 200,
76
- damping: 15,
77
- }}
78
- whileHover={{
79
- scale: 1.1,
80
- filter: 'brightness(1.2)',
81
- transition: {
82
- type: 'spring',
83
- stiffness: 400,
84
- damping: 10,
85
- },
86
- }}
87
- />
88
- <motion.div
89
- initial={{ opacity: 0, y: 20 }}
90
- animate={{ opacity: 1, y: 0 }}
91
- transition={{ duration: 0.5, delay: 0.4 }}
92
- >
93
- <Typography
94
- variant="h3"
95
- component="h1"
96
- align="center"
97
- gutterBottom
98
- sx={{
99
- mb: 0.25,
100
- color: 'white',
101
- textShadow: '0 2px 4px rgba(0,0,0,0.2)',
102
- }}
103
- >
104
- React Broadcast Sync Demo
105
- </Typography>
106
- </motion.div>
107
- <motion.div
108
- initial={{ opacity: 0, y: 20 }}
109
- animate={{ opacity: 1, y: 0 }}
110
- transition={{ duration: 0.5, delay: 0.6 }}
111
- >
112
- <Typography
113
- variant="h6"
114
- align="center"
115
- color="text.secondary"
116
- paragraph
117
- sx={{ mb: 1 }}
118
- >
119
- Real-time state synchronization across browser tabs
120
- </Typography>
121
- <Box
122
- sx={{
123
- display: 'flex',
124
- alignItems: 'center',
125
- justifyContent: 'center',
126
- gap: 1,
127
- mb: 1,
128
- }}
129
- >
130
- <Typography
131
- variant="body2"
132
- color="text.secondary"
133
- sx={{ display: 'flex', alignItems: 'center' }}
134
- >
135
- Open a new tab to see the synchronization in action
136
- </Typography>
137
- <motion.div whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.95 }}>
138
- <IconButton
139
- onClick={() => window.open(window.location.href, '_blank')}
140
- color="primary"
141
- size="small"
142
- >
143
- <OpenInNewIcon />
144
- </IconButton>
145
- </motion.div>
146
- </Box>
147
- </motion.div>
148
- </Box>
149
- <Grid container spacing={{ xs: 2, md: 3 }}>
150
- <Grid item xs={12} md={4}>
151
- <motion.div
152
- initial={{ scale: 0.8, opacity: 0, y: 20 }}
153
- animate={{ scale: 1, opacity: 1, y: 0 }}
154
- transition={{
155
- type: 'spring',
156
- stiffness: 300,
157
- damping: 20,
158
- delay: 0.8,
159
- }}
160
- style={{ height: '100%' }}
161
- >
162
- <Counter />
163
- </motion.div>
164
- </Grid>
165
- <Grid item xs={12} md={4}>
166
- <motion.div
167
- initial={{ scale: 0.8, opacity: 0, y: 20 }}
168
- animate={{ scale: 1, opacity: 1, y: 0 }}
169
- transition={{
170
- type: 'spring',
171
- stiffness: 300,
172
- damping: 20,
173
- delay: 0.9,
174
- }}
175
- style={{ height: '100%' }}
176
- >
177
- <TextSync />
178
- </motion.div>
179
- </Grid>
180
- <Grid item xs={12} md={4}>
181
- <motion.div
182
- initial={{ scale: 0.8, opacity: 0, y: 20 }}
183
- animate={{ scale: 1, opacity: 1, y: 0 }}
184
- transition={{
185
- type: 'spring',
186
- stiffness: 300,
187
- damping: 20,
188
- delay: 1.0,
189
- }}
190
- style={{ height: '100%' }}
191
- >
192
- <TodoList />
193
- </motion.div>
194
- </Grid>
195
- </Grid>
196
- </motion.div>
197
- </Container>
198
-
199
- <motion.div
200
- initial={{ opacity: 0, y: 20 }}
201
- animate={{ opacity: 1, y: 0 }}
202
- transition={{ duration: 0.5, delay: 1.2 }}
203
- >
204
- <Box
205
- component="footer"
206
- sx={{
207
- py: { xs: 1, md: 1.5 },
208
- px: { xs: 1, md: 1.5 },
209
- backgroundColor: 'background.paper',
210
- borderTop: 1,
211
- borderColor: 'divider',
212
- mt: 'auto',
213
- }}
214
- >
215
- <Container maxWidth="lg">
216
- <Box
217
- sx={{
218
- display: 'flex',
219
- flexDirection: 'column',
220
- alignItems: 'center',
221
- gap: { xs: 1, md: 1.5 },
222
- }}
223
- >
224
- <Typography variant="subtitle1" color="text.secondary" gutterBottom sx={{ mb: 0 }}>
225
- Connect with us
226
- </Typography>
227
- <Box sx={{ display: 'flex', gap: { xs: 1, md: 1.5 } }}>
228
- <motion.div whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.95 }}>
229
- <IconButton
230
- component={Link}
231
- href="https://github.com/IdanShalem/react-broadcast-sync"
232
- target="_blank"
233
- rel="noopener noreferrer"
234
- color="primary"
235
- size="large"
236
- >
237
- <GitHubIcon fontSize="large" />
238
- </IconButton>
239
- </motion.div>
240
- <motion.div whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.95 }}>
241
- <IconButton
242
- component={Link}
243
- href="https://www.npmjs.com/package/react-broadcast-sync"
244
- target="_blank"
245
- rel="noopener noreferrer"
246
- color="secondary"
247
- size="large"
248
- >
249
- <CodeIcon fontSize="large" />
250
- </IconButton>
251
- </motion.div>
252
- <motion.div whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.95 }}>
253
- <IconButton
254
- component={Link}
255
- href="https://www.linkedin.com/in/idan-shalem-3a1781169/"
256
- target="_blank"
257
- rel="noopener noreferrer"
258
- color="primary"
259
- size="large"
260
- >
261
- <LinkedInIcon fontSize="large" />
262
- </IconButton>
263
- </motion.div>
264
- </Box>
265
- <Typography variant="caption" color="text.secondary" sx={{ mt: 0.5 }}>
266
- © {new Date().getFullYear()} Idan Shalem. All rights reserved.
267
- </Typography>
268
- </Box>
269
- </Container>
270
- </Box>
271
- </motion.div>
272
- </Box>
273
- </ThemeProvider>
274
- );
275
- };
276
-
277
- export default App;
@@ -1,63 +0,0 @@
1
- import { Box, Typography, Paper, IconButton } from '@mui/material';
2
- import AddIcon from '@mui/icons-material/Add';
3
- import RemoveIcon from '@mui/icons-material/Remove';
4
- import { useCounterBroadcast } from '../hooks/useCounterBroadcast';
5
-
6
- export const Counter = () => {
7
- const { count, increment, decrement } = useCounterBroadcast();
8
-
9
- return (
10
- <Paper
11
- elevation={3}
12
- sx={{
13
- p: 2,
14
- height: '300px',
15
- display: 'flex',
16
- flexDirection: 'column',
17
- alignItems: 'center',
18
- justifyContent: 'start',
19
- background: 'linear-gradient(145deg, rgba(76, 175, 80, 0.1), rgba(76, 175, 80, 0.05))',
20
- }}
21
- >
22
- <Typography
23
- variant="h6"
24
- gutterBottom
25
- sx={{
26
- color: 'white',
27
- textShadow: '0 2px 4px rgba(0,0,0,0.2)',
28
- }}
29
- >
30
- Counter
31
- </Typography>
32
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
33
- <IconButton
34
- onClick={decrement}
35
- sx={{
36
- bgcolor: 'primary.main',
37
- color: 'white',
38
- '&:hover': {
39
- bgcolor: 'primary.dark',
40
- },
41
- }}
42
- >
43
- <RemoveIcon />
44
- </IconButton>
45
- <Typography variant="h3" component="div" sx={{ minWidth: '80px', textAlign: 'center' }}>
46
- {count}
47
- </Typography>
48
- <IconButton
49
- onClick={increment}
50
- sx={{
51
- bgcolor: 'primary.main',
52
- color: 'white',
53
- '&:hover': {
54
- bgcolor: 'primary.dark',
55
- },
56
- }}
57
- >
58
- <AddIcon />
59
- </IconButton>
60
- </Box>
61
- </Paper>
62
- );
63
- };
@@ -1,61 +0,0 @@
1
- import { Box, Typography, TextField, Paper } from '@mui/material';
2
- import { useTextBroadcast } from '../hooks/useTextBroadcast';
3
-
4
- export const TextSync = () => {
5
- const { localText, handleChange } = useTextBroadcast();
6
-
7
- return (
8
- <Paper
9
- elevation={3}
10
- sx={{
11
- p: 2,
12
- height: '300px',
13
- display: 'flex',
14
- flexDirection: 'column',
15
- position: 'relative',
16
- background: 'linear-gradient(145deg, rgba(76, 175, 80, 0.1), rgba(76, 175, 80, 0.05))',
17
- }}
18
- >
19
- <Typography
20
- variant="h6"
21
- gutterBottom
22
- sx={{
23
- color: 'white',
24
- textShadow: '0 2px 4px rgba(0,0,0,0.2)',
25
- }}
26
- >
27
- Text Synchronization
28
- </Typography>
29
- <Box
30
- sx={{
31
- flex: 1,
32
- display: 'flex',
33
- flexDirection: 'column',
34
- gap: 1,
35
- pt: 1,
36
- paddingTop: 0,
37
- }}
38
- >
39
- <TextField
40
- fullWidth
41
- value={localText}
42
- onChange={handleChange}
43
- placeholder="Type something..."
44
- variant="outlined"
45
- size="small"
46
- sx={{
47
- '& .MuiOutlinedInput-root': {
48
- height: '80px',
49
- '& textarea': {
50
- height: '100% !important',
51
- padding: '8px 12px',
52
- },
53
- },
54
- }}
55
- multiline
56
- rows={3}
57
- />
58
- </Box>
59
- </Paper>
60
- );
61
- };
@@ -1,186 +0,0 @@
1
- import {
2
- Box,
3
- Typography,
4
- TextField,
5
- Paper,
6
- List,
7
- ListItem,
8
- IconButton,
9
- Checkbox,
10
- Fade,
11
- CircularProgress,
12
- } from '@mui/material';
13
- import DeleteIcon from '@mui/icons-material/Delete';
14
- import CheckCircleIcon from '@mui/icons-material/CheckCircle';
15
- import { useTodoBroadcast } from '../hooks/useTodoBroadcast';
16
- import { useTheme } from '@mui/material/styles';
17
-
18
- interface Todo {
19
- id: string;
20
- text: string;
21
- completed: boolean;
22
- }
23
-
24
- export const TodoList = () => {
25
- const {
26
- todos,
27
- newTodo,
28
- handleNewTodoChange,
29
- handleAddTodo,
30
- handleToggleTodo,
31
- handleDeleteTodo,
32
- handleScrollSync,
33
- hoveredTodoId,
34
- scrollContainerRef,
35
- handleTodoMouseEnter,
36
- handleTodoMouseLeave,
37
- isSyncing,
38
- showCheckmark,
39
- } = useTodoBroadcast();
40
- const theme = useTheme();
41
-
42
- return (
43
- <Paper
44
- elevation={3}
45
- sx={{
46
- p: 2,
47
- height: '300px',
48
- display: 'flex',
49
- flexDirection: 'column',
50
- position: 'relative',
51
- background: 'linear-gradient(145deg, rgba(94, 193, 61, 0.1), rgba(63, 173, 58, 0.05))',
52
- }}
53
- >
54
- <Typography
55
- variant="h6"
56
- gutterBottom
57
- sx={{
58
- color: theme.palette.text.primary,
59
- textShadow: '0 2px 4px rgba(0,0,0,0.2)',
60
- }}
61
- >
62
- Todo List
63
- </Typography>
64
- <Box
65
- component="form"
66
- onSubmit={handleAddTodo}
67
- sx={{
68
- display: 'flex',
69
- gap: 1,
70
- mb: 1,
71
- }}
72
- >
73
- <TextField
74
- fullWidth
75
- value={newTodo}
76
- onChange={handleNewTodoChange}
77
- placeholder="Add a new todo..."
78
- variant="outlined"
79
- size="small"
80
- InputProps={{
81
- style: {
82
- color: theme.palette.text.primary,
83
- },
84
- }}
85
- />
86
- </Box>
87
- <Box
88
- ref={scrollContainerRef}
89
- onScroll={handleScrollSync}
90
- sx={{
91
- flex: 1,
92
- overflow: 'auto',
93
- position: 'relative',
94
- '&::-webkit-scrollbar': {
95
- width: '8px',
96
- },
97
- '&::-webkit-scrollbar-track': {
98
- background: 'transparent',
99
- },
100
- '&::-webkit-scrollbar-thumb': {
101
- background: 'rgba(255, 255, 255, 0.08)',
102
- borderRadius: '4px',
103
- },
104
- }}
105
- >
106
- <List>
107
- {todos.map((todo: Todo) => (
108
- <ListItem
109
- key={todo.id}
110
- onMouseEnter={() => handleTodoMouseEnter(todo.id)}
111
- onMouseLeave={handleTodoMouseLeave}
112
- sx={{
113
- opacity: hoveredTodoId === todo.id ? 0.7 : 1,
114
- transition: 'opacity 0.2s',
115
- }}
116
- secondaryAction={
117
- <IconButton
118
- edge="end"
119
- onClick={() => handleDeleteTodo(todo.id)}
120
- size="small"
121
- sx={{ color: theme.palette.primary.main }}
122
- >
123
- <DeleteIcon />
124
- </IconButton>
125
- }
126
- >
127
- <Checkbox
128
- checked={todo.completed}
129
- onChange={() => handleToggleTodo(todo.id)}
130
- size="small"
131
- sx={{
132
- color: theme.palette.primary.main,
133
- '&.Mui-checked': {
134
- color: theme.palette.primary.main,
135
- },
136
- }}
137
- />
138
- <Typography
139
- sx={{
140
- textDecoration: todo.completed ? 'line-through' : 'none',
141
- color: todo.completed ? theme.palette.text.secondary : theme.palette.text.primary,
142
- }}
143
- >
144
- {todo.text}
145
- </Typography>
146
- </ListItem>
147
- ))}
148
- </List>
149
- </Box>
150
- {/* Sync Indicator */}
151
- <Fade in={isSyncing || showCheckmark}>
152
- <Box
153
- sx={{
154
- position: 'absolute',
155
- top: 8,
156
- right: 8,
157
- display: 'flex',
158
- alignItems: 'center',
159
- gap: 1,
160
- bgcolor: 'background.paper',
161
- borderRadius: 1,
162
- px: 1.5,
163
- py: 0.5,
164
- boxShadow: 1,
165
- }}
166
- >
167
- {isSyncing ? (
168
- <>
169
- <CircularProgress size={16} thickness={4} />
170
- <Typography variant="caption" color="text.secondary">
171
- Syncing...
172
- </Typography>
173
- </>
174
- ) : showCheckmark ? (
175
- <CheckCircleIcon
176
- sx={{
177
- color: theme.palette.success.main,
178
- fontSize: 20,
179
- }}
180
- />
181
- ) : null}
182
- </Box>
183
- </Fade>
184
- </Paper>
185
- );
186
- };