@dxtmisha/wiki 0.24.0 → 0.24.2

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.
@@ -0,0 +1,344 @@
1
+ import {Meta} from '@storybook/addon-docs/blocks'
2
+
3
+ <Meta title='@dxtmisha/functional/en/Composables/useBroadcastValueRef'/>
4
+
5
+ # Composable useBroadcastValueRef
6
+
7
+ Composable for creating a reactive variable synchronized between browser tabs via Broadcast Channel API. Automatically manages data transmission between open tabs of the same domain in real-time without using localStorage. Perfect for synchronizing application state, notifications, and data exchange between tabs.
8
+
9
+ ## Key Features
10
+
11
+ - **Real-time synchronization** — instant data transmission between browser tabs
12
+ - **Broadcast Channel API** — uses native browser API for efficient communication
13
+ - **Automatic synchronization** — changes in one tab automatically reflect in all others
14
+ - **Singleton pattern** — channel reuse for same names
15
+ - **Unique identification** — each browser session gets unique ID
16
+ - **Type safety** — full TypeScript support with generics
17
+ - **Default values** — supports initial values and factory functions
18
+ - **Session isolation** — data not transmitted between different browser sessions
19
+
20
+ ## Function
21
+
22
+ ### `useBroadcastValueRef`
23
+
24
+ Creates reactive variable synchronized between tabs via Broadcast Channel.
25
+
26
+ **Parameters:**
27
+ - `name: string` — channel name for communication between tabs
28
+ - `defaultValue?: T | string | (() => (T | string))` — default value or function to generate it (optional)
29
+
30
+ **Returns:** `Ref<T | string | undefined>` — Vue reactive variable synchronized between tabs
31
+
32
+ ```javascript
33
+ import { useBroadcastValueRef } from '@dxtmisha/functional'
34
+
35
+ // Simple usage
36
+ const sharedCounter = useBroadcastValueRef('counter')
37
+
38
+ // With default value
39
+ const activeTab = useBroadcastValueRef('active-tab', 'home')
40
+
41
+ // With factory function
42
+ const sessionId = useBroadcastValueRef('session', () => Math.random().toString(36))
43
+ ```
44
+
45
+ ## Basic Usage
46
+
47
+ ### Basic Synchronization
48
+
49
+ ```javascript
50
+ import { useBroadcastValueRef } from '@dxtmisha/functional'
51
+
52
+ // Create synchronized variable
53
+ const counter = useBroadcastValueRef('counter', 0)
54
+
55
+ // In first tab
56
+ counter.value = 5
57
+
58
+ // In second tab automatically updates
59
+ console.log(counter.value) // 5
60
+
61
+ // Change in any tab reflects in all others
62
+ counter.value = 10
63
+ // All tabs get counter.value === 10 instantly
64
+ ```
65
+
66
+ ### Singleton Pattern
67
+
68
+ ```javascript
69
+ // Repeated calls with same name return existing channel
70
+ const channel1 = useBroadcastValueRef('notifications', null)
71
+ const channel2 = useBroadcastValueRef('notifications', null)
72
+
73
+ console.log(channel1 === channel2) // true - same ref
74
+
75
+ channel1.value = { type: 'info', message: 'Hello!' }
76
+ console.log(channel2.value) // { type: 'info', message: 'Hello!' }
77
+ ```
78
+
79
+ ## Usage in Components
80
+
81
+ ### Notification Synchronization
82
+
83
+ ```javascript
84
+ import { useBroadcastValueRef } from '@dxtmisha/functional'
85
+
86
+ export default {
87
+ setup() {
88
+ const notification = useBroadcastValueRef('notification', null)
89
+
90
+ const showNotification = (message, type = 'info') => {
91
+ notification.value = {
92
+ id: Date.now(),
93
+ type,
94
+ message,
95
+ timestamp: new Date()
96
+ }
97
+ }
98
+
99
+ return {
100
+ notification,
101
+ showNotification
102
+ }
103
+ }
104
+ }
105
+
106
+ // Template:
107
+ // <div v-if="notification" class="notification">
108
+ // <span>{{ notification.message }}</span>
109
+ // <button @click="notification = null">×</button>
110
+ // </div>
111
+ //
112
+ // Notification shows in all open tabs
113
+ ```
114
+
115
+ ### Shared Counter
116
+
117
+ ```javascript
118
+ import { useBroadcastValueRef } from '@dxtmisha/functional'
119
+
120
+ export default {
121
+ setup() {
122
+ const cartCount = useBroadcastValueRef('cart-count', 0)
123
+
124
+ const addToCart = () => {
125
+ cartCount.value = (cartCount.value || 0) + 1
126
+ }
127
+
128
+ const removeFromCart = () => {
129
+ if (cartCount.value > 0) {
130
+ cartCount.value--
131
+ }
132
+ }
133
+
134
+ return {
135
+ cartCount,
136
+ addToCart,
137
+ removeFromCart
138
+ }
139
+ }
140
+ }
141
+
142
+ // Template:
143
+ // <div>
144
+ // <span class="badge">{{ cartCount }}</span>
145
+ // <button @click="addToCart">Add</button>
146
+ // <button @click="removeFromCart">Remove</button>
147
+ // </div>
148
+ //
149
+ // Counter syncs between all tabs
150
+ ```
151
+
152
+ ## State Synchronization
153
+
154
+ ### Active Tab
155
+
156
+ ```javascript
157
+ import { useBroadcastValueRef } from '@dxtmisha/functional'
158
+ import { watch } from 'vue'
159
+
160
+ export default {
161
+ setup() {
162
+ const activeTab = useBroadcastValueRef('active-nav-tab', 'home')
163
+
164
+ const setActiveTab = (tab) => {
165
+ activeTab.value = tab
166
+ }
167
+
168
+ // Track changes from other tabs
169
+ watch(activeTab, (newTab) => {
170
+ console.log(`Active tab changed to: ${newTab}`)
171
+ })
172
+
173
+ return {
174
+ activeTab,
175
+ setActiveTab
176
+ }
177
+ }
178
+ }
179
+
180
+ // When switching tab in one window,
181
+ // all other windows synchronize
182
+ ```
183
+
184
+ ### Authentication Status
185
+
186
+ ```javascript
187
+ import { useBroadcastValueRef } from '@dxtmisha/functional'
188
+
189
+ export default {
190
+ setup() {
191
+ const isAuthenticated = useBroadcastValueRef('auth-status', false)
192
+ const currentUser = useBroadcastValueRef('current-user', null)
193
+
194
+ const login = (userData) => {
195
+ currentUser.value = userData
196
+ isAuthenticated.value = true
197
+ }
198
+
199
+ const logout = () => {
200
+ currentUser.value = null
201
+ isAuthenticated.value = false
202
+ }
203
+
204
+ return {
205
+ isAuthenticated,
206
+ currentUser,
207
+ login,
208
+ logout
209
+ }
210
+ }
211
+ }
212
+
213
+ // When logging in/out in one tab,
214
+ // all other tabs update instantly
215
+ ```
216
+
217
+ ## Differences from useStorageRef
218
+
219
+ ```javascript
220
+ // useStorageRef - via localStorage (persistent storage)
221
+ const persistentData = useStorageRef('theme', 'light')
222
+ // - Data saved in localStorage
223
+ // - Synchronization via storage event
224
+ // - Slower data transmission
225
+ // - Data remains after browser closes
226
+
227
+ // useBroadcastValueRef - via Broadcast Channel (in memory)
228
+ const realtimeData = useBroadcastValueRef('active-users', [])
229
+ // - Data in memory (not saved)
230
+ // - Instant synchronization
231
+ // - Fast data transmission
232
+ // - Data lost when all tabs close
233
+ // - Isolation between browser sessions
234
+ ```
235
+
236
+ ## Working with Data Types
237
+
238
+ ```javascript
239
+ // Numbers
240
+ const counter = useBroadcastValueRef<number>('counter', 0)
241
+ counter.value = 42
242
+
243
+ // Strings
244
+ const message = useBroadcastValueRef<string>('message', '')
245
+ message.value = 'Hello from another tab!'
246
+
247
+ // Objects
248
+ const userState = useBroadcastValueRef('user-state', {
249
+ id: null,
250
+ name: '',
251
+ online: false
252
+ })
253
+ userState.value = { id: 1, name: 'John', online: true }
254
+
255
+ // Arrays
256
+ const activeUsers = useBroadcastValueRef<number[]>('active-users', [])
257
+ activeUsers.value = [1, 2, 3, 4, 5]
258
+ ```
259
+
260
+ ## Usage Examples
261
+
262
+ ### Cross-tab Chat
263
+
264
+ ```javascript
265
+ const chatMessages = useBroadcastValueRef('chat-messages', [])
266
+
267
+ const sendMessage = (text) => {
268
+ const message = {
269
+ id: Date.now(),
270
+ text,
271
+ timestamp: new Date()
272
+ }
273
+
274
+ chatMessages.value = [...(chatMessages.value || []), message]
275
+ }
276
+
277
+ // Messages appear in all open tabs instantly
278
+ ```
279
+
280
+ ### Playback Synchronization
281
+
282
+ ```javascript
283
+ const playerState = useBroadcastValueRef('player', {
284
+ isPlaying: false,
285
+ currentTime: 0,
286
+ track: null
287
+ })
288
+
289
+ const play = () => {
290
+ playerState.value = { ...playerState.value, isPlaying: true }
291
+ }
292
+
293
+ const pause = () => {
294
+ playerState.value = { ...playerState.value, isPlaying: false }
295
+ }
296
+
297
+ // Controlling player in one tab
298
+ // affects players in all others
299
+ ```
300
+
301
+ ### Shared Filters
302
+
303
+ ```javascript
304
+ const filters = useBroadcastValueRef('search-filters', {
305
+ category: 'all',
306
+ priceMin: 0,
307
+ priceMax: 10000
308
+ })
309
+
310
+ const updateFilter = (key, value) => {
311
+ filters.value = { ...filters.value, [key]: value }
312
+ }
313
+
314
+ // Filter changes synchronize
315
+ // between all open search pages
316
+ ```
317
+
318
+ ## Unique Session Identification
319
+
320
+ Composable automatically creates unique ID for each browser session and saves it in localStorage:
321
+
322
+ ```javascript
323
+ // On first run, random ID is generated
324
+ // broadcast__name_1234567__counter
325
+ // ^^^^^^^^^ - unique session ID
326
+
327
+ // This ID is reused for all channels within one session
328
+ // Different browser sessions have different IDs and don't overlap
329
+ ```
330
+
331
+ ## Broadcast Channel API Features
332
+
333
+ ```javascript
334
+ // Broadcast Channel works only:
335
+ // - Within same origin (protocol + domain + port)
336
+ // - Between tabs of same browser
337
+ // - In same browser session
338
+
339
+ // Does NOT work:
340
+ // - Between different browsers
341
+ // - Between different domains
342
+ // - In incognito mode between regular tabs
343
+ ```
344
+
@@ -0,0 +1,348 @@
1
+ import {Meta} from '@storybook/addon-docs/blocks'
2
+
3
+ <Meta title='@dxtmisha/functional/en/Composables/useCookieRef'/>
4
+
5
+ # Composable useCookieRef
6
+
7
+ Composable for creating a reactive variable synchronized with browser cookies. Automatically manages reading and writing values to cookies with support for cross-tab synchronization via Broadcast Channel API, expiration settings, and singleton pattern for efficient reuse.
8
+
9
+ ## Key Features
10
+
11
+ - **Two-way synchronization** — automatic sync between ref and cookies
12
+ - **Cross-tab synchronization** — changes in one tab instantly reflect in all others via Broadcast Channel
13
+ - **Automatic persistence** — ref changes automatically saved to cookies
14
+ - **Expiration settings** — supports expires, max-age and other cookie options
15
+ - **Singleton pattern** — reuses ref for same cookie names
16
+ - **Type safety** — full TypeScript support with generics
17
+ - **Default values** — supports initial values and factory functions
18
+ - **Cookie integration** — uses Cookie class for cookie management
19
+
20
+ ## Function
21
+
22
+ ### `useCookieRef`
23
+
24
+ Creates reactive variable synchronized with browser cookies.
25
+
26
+ **Parameters:**
27
+ - `name: string` — cookie name
28
+ - `defaultValue?: T | string | (() => (T | string))` — default value or function to generate it (optional)
29
+ - `options?: CookieOptions` — additional cookie parameters (optional)
30
+
31
+ **Returns:** `Ref<T | string | undefined>` — Vue reactive variable linked to cookie
32
+
33
+ **CookieOptions:**
34
+ - `expires?: number | Date` — expiration date
35
+ - `path?: string` — path for cookie (default '/')
36
+ - `domain?: string` — domain for cookie
37
+ - `secure?: boolean` — use HTTPS only
38
+ - `sameSite?: 'Strict' | 'Lax' | 'None'` — SameSite policy
39
+
40
+ ```javascript
41
+ import { useCookieRef } from '@dxtmisha/functional'
42
+
43
+ // Simple usage
44
+ const userToken = useCookieRef('token')
45
+
46
+ // With default value
47
+ const theme = useCookieRef('theme', 'light')
48
+
49
+ // With options (expires in 7 days)
50
+ const rememberMe = useCookieRef('remember', true, {
51
+ expires: 7,
52
+ path: '/',
53
+ sameSite: 'Lax'
54
+ })
55
+ ```
56
+
57
+ ## Basic Usage
58
+
59
+ ### Basic Synchronization
60
+
61
+ ```javascript
62
+ import { useCookieRef } from '@dxtmisha/functional'
63
+
64
+ // Create ref synchronized with cookie
65
+ const userTheme = useCookieRef('theme', 'light')
66
+
67
+ // When ref changes - cookie automatically updates
68
+ userTheme.value = 'dark'
69
+ // document.cookie contains: 'theme=dark'
70
+
71
+ // On page reload, value is restored
72
+ console.log(userTheme.value) // 'dark'
73
+ ```
74
+
75
+ ### Singleton Pattern
76
+
77
+ ```javascript
78
+ // Repeated calls with same name return existing ref
79
+ const token1 = useCookieRef('auth-token', '')
80
+ const token2 = useCookieRef('auth-token', '')
81
+
82
+ console.log(token1 === token2) // true - same ref
83
+
84
+ token1.value = 'abc123'
85
+ console.log(token2.value) // 'abc123' - both point to same ref
86
+ ```
87
+
88
+ ### Cross-tab Synchronization
89
+
90
+ ```javascript
91
+ import { useCookieRef } from '@dxtmisha/functional'
92
+
93
+ // In first tab
94
+ const userState = useCookieRef('user-online', 'active')
95
+ userState.value = 'away'
96
+
97
+ // In second tab automatically updates instantly
98
+ // userState.value === 'away' (thanks to Broadcast Channel)
99
+
100
+ // In third tab
101
+ const sameState = useCookieRef('user-online', 'active')
102
+ console.log(sameState.value) // 'away'
103
+
104
+ // Changes in any tab sync with all others
105
+ ```
106
+
107
+ ## Usage in Components
108
+
109
+ ### User Settings
110
+
111
+ ```javascript
112
+ import { useCookieRef } from '@dxtmisha/functional'
113
+
114
+ export default {
115
+ setup() {
116
+ const theme = useCookieRef('user-theme', 'light', {
117
+ expires: 365 // 1 year
118
+ })
119
+
120
+ const language = useCookieRef('user-language', 'en', {
121
+ expires: 365
122
+ })
123
+
124
+ const fontSize = useCookieRef('font-size', 16, {
125
+ expires: 30 // 30 days
126
+ })
127
+
128
+ return {
129
+ theme,
130
+ language,
131
+ fontSize
132
+ }
133
+ }
134
+ }
135
+
136
+ // Template:
137
+ // <div>
138
+ // <select v-model="theme">
139
+ // <option value="light">Light</option>
140
+ // <option value="dark">Dark</option>
141
+ // </select>
142
+ // <select v-model="language">
143
+ // <option value="en">English</option>
144
+ // <option value="ru">Русский</option>
145
+ // </select>
146
+ // </div>
147
+ ```
148
+
149
+ ### Authentication with "Remember Me"
150
+
151
+ ```javascript
152
+ import { useCookieRef } from '@dxtmisha/functional'
153
+
154
+ export default {
155
+ setup() {
156
+ const authToken = useCookieRef('auth-token', '', {
157
+ expires: 7, // 7 days
158
+ secure: true,
159
+ sameSite: 'Strict'
160
+ })
161
+
162
+ const rememberMe = useCookieRef('remember-me', false)
163
+
164
+ const login = (token) => {
165
+ const expires = rememberMe.value ? 30 : 1 // 30 days or 1 day
166
+ authToken.value = token
167
+
168
+ // Update cookie options
169
+ useCookieRef('auth-token', token, {
170
+ expires,
171
+ secure: true,
172
+ sameSite: 'Strict'
173
+ })
174
+ }
175
+
176
+ const logout = () => {
177
+ authToken.value = ''
178
+ rememberMe.value = false
179
+ }
180
+
181
+ return {
182
+ authToken,
183
+ rememberMe,
184
+ login,
185
+ logout
186
+ }
187
+ }
188
+ }
189
+ ```
190
+
191
+ ## Cookie Settings
192
+
193
+ ### Expiration
194
+
195
+ ```javascript
196
+ // Expires in days
197
+ const token = useCookieRef('token', '', {
198
+ expires: 7 // deleted after 7 days
199
+ })
200
+
201
+ // Expires as Date
202
+ const session = useCookieRef('session', '', {
203
+ expires: new Date('2025-12-31')
204
+ })
205
+
206
+ // Without expires (session cookie, deleted on browser close)
207
+ const tempData = useCookieRef('temp', '')
208
+ ```
209
+
210
+ ### Security
211
+
212
+ ```javascript
213
+ // HTTPS only
214
+ const secureToken = useCookieRef('secure-token', '', {
215
+ secure: true,
216
+ sameSite: 'Strict'
217
+ })
218
+
219
+ // Cross-site requests
220
+ const apiToken = useCookieRef('api-token', '', {
221
+ secure: true,
222
+ sameSite: 'None' // requires secure: true
223
+ })
224
+ ```
225
+
226
+ ### Path and Domain
227
+
228
+ ```javascript
229
+ // For specific path
230
+ const adminPrefs = useCookieRef('admin-prefs', {}, {
231
+ path: '/admin'
232
+ })
233
+
234
+ // For subdomain
235
+ const sharedData = useCookieRef('shared', '', {
236
+ domain: '.example.com' // accessible for all subdomains
237
+ })
238
+ ```
239
+
240
+ ## Working with Data Types
241
+
242
+ ```javascript
243
+ // Strings
244
+ const username = useCookieRef<string>('username', '')
245
+ username.value = 'John'
246
+
247
+ // Numbers (automatic serialization)
248
+ const count = useCookieRef<number>('count', 0)
249
+ count.value = 42
250
+
251
+ // Booleans
252
+ const accepted = useCookieRef<boolean>('accepted', false)
253
+ accepted.value = true
254
+
255
+ // Objects (JSON serialization)
256
+ const userPrefs = useCookieRef('prefs', {
257
+ theme: 'dark',
258
+ notifications: true
259
+ })
260
+ userPrefs.value = { theme: 'light', notifications: false }
261
+ ```
262
+
263
+ ## Integration with Cookie Class
264
+
265
+ Composable uses `Cookie` class for cookie management:
266
+
267
+ ```javascript
268
+ import { Cookie } from '@dxtmisha/functional'
269
+
270
+ // Direct class usage
271
+ const cookie = new Cookie('theme')
272
+ cookie.set('dark', { expires: 30 })
273
+ console.log(cookie.get()) // 'dark'
274
+
275
+ // useCookieRef automatically synchronizes
276
+ const themeRef = useCookieRef('theme')
277
+ console.log(themeRef.value) // 'dark'
278
+
279
+ // Changes via Cookie reflect in ref (via Broadcast Channel)
280
+ cookie.set('light')
281
+ // themeRef.value automatically updates in all tabs
282
+ ```
283
+
284
+ ## Usage Examples
285
+
286
+ ### Cookie Consent (GDPR)
287
+
288
+ ```javascript
289
+ const cookieConsent = useCookieRef('cookie-consent', false, {
290
+ expires: 365,
291
+ sameSite: 'Lax'
292
+ })
293
+
294
+ const acceptCookies = () => {
295
+ cookieConsent.value = true
296
+ }
297
+
298
+ // In template:
299
+ // <div v-if="!cookieConsent" class="cookie-banner">
300
+ // <p>We use cookies...</p>
301
+ // <button @click="acceptCookies">Accept</button>
302
+ // </div>
303
+ ```
304
+
305
+ ### Shopping Cart
306
+
307
+ ```javascript
308
+ const cart = useCookieRef<CartItem[]>('shopping-cart', [], {
309
+ expires: 7
310
+ })
311
+
312
+ const addToCart = (item) => {
313
+ cart.value = [...cart.value, item]
314
+ }
315
+
316
+ const removeFromCart = (itemId) => {
317
+ cart.value = cart.value.filter(item => item.id !== itemId)
318
+ }
319
+
320
+ // Cart syncs between tabs automatically
321
+ ```
322
+
323
+ ### Recently Viewed Tracking
324
+
325
+ ```javascript
326
+ const recentlyViewed = useCookieRef<number[]>('recently-viewed', [], {
327
+ expires: 30
328
+ })
329
+
330
+ const addToRecent = (productId) => {
331
+ const recent = [productId, ...recentlyViewed.value.filter(id => id !== productId)]
332
+ recentlyViewed.value = recent.slice(0, 10) // Keep last 10
333
+ }
334
+ ```
335
+
336
+ ## Broadcast Channel Synchronization Benefits
337
+
338
+ **Unlike regular ref with cookie:**
339
+ - ✅ Instant cross-tab synchronization (no reload required)
340
+ - ✅ Real-time updates (no delays)
341
+ - ✅ Efficient data transmission (not via localStorage)
342
+ - ✅ Browser session isolation
343
+
344
+ **How it works:**
345
+ 1. Change in one tab → saved to cookie
346
+ 2. Via Broadcast Channel (`__cookie:${name}`) message is sent
347
+ 3. All other tabs receive update instantly
348
+ 4. Ref in all tabs updates automatically