@dxtmisha/wiki 0.24.0 → 0.24.1
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/package.json +1 -1
- package/src/media/functional/en/about.mdx +55 -0
- package/src/media/functional/en/dataStorage.mdx +61 -83
- package/src/media/functional/en/useApiRef.mdx +517 -0
- package/src/media/functional/en/useBroadcastValueRef.mdx +344 -0
- package/src/media/functional/en/useCookieRef.mdx +348 -0
- package/src/media/functional/en/useGeoIntlRef.mdx +288 -0
- package/src/media/functional/en/useHashRef.mdx +302 -0
- package/src/media/functional/en/useLazyRef.mdx +329 -0
- package/src/media/functional/en/useLoadingRef.mdx +159 -0
- package/src/media/functional/en/useSessionRef.mdx +248 -0
- package/src/media/functional/en/useStorageRef.mdx +242 -0
- package/src/media/functional/en/useTranslateRef.mdx +312 -0
- package/src/media/functional/ru/about.mdx +55 -0
- package/src/media/functional/ru/dataStorage.mdx +59 -81
- package/src/media/functional/ru/useApiRef.mdx +517 -0
- package/src/media/functional/ru/useBroadcastValueRef.mdx +344 -0
- package/src/media/functional/ru/useCookieRef.mdx +348 -0
- package/src/media/functional/ru/useGeoIntlRef.mdx +288 -0
- package/src/media/functional/ru/useHashRef.mdx +302 -0
- package/src/media/functional/ru/useLazyRef.mdx +329 -0
- package/src/media/functional/ru/useLoadingRef.mdx +159 -0
- package/src/media/functional/ru/useSessionRef.mdx +248 -0
- package/src/media/functional/ru/useStorageRef.mdx +242 -0
- package/src/media/functional/ru/useTranslateRef.mdx +312 -0
|
@@ -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
|