@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.
@@ -0,0 +1,344 @@
1
+ import {Meta} from '@storybook/addon-docs/blocks'
2
+
3
+ <Meta title='@dxtmisha/functional/ru/Composables/useBroadcastValueRef'/>
4
+
5
+ # Композабл useBroadcastValueRef
6
+
7
+ Композабл для создания реактивной переменной, синхронизированной между вкладками браузера через Broadcast Channel API. Автоматически управляет передачей данных между открытыми вкладками одного домена в реальном времени без использования localStorage. Идеально подходит для синхронизации состояния приложения, уведомлений и обмена данными между вкладками.
8
+
9
+ ## Основные возможности
10
+
11
+ - **Реальная синхронизация** — мгновенная передача данных между вкладками браузера
12
+ - **Broadcast Channel API** — использует нативный браузерный API для эффективной коммуникации
13
+ - **Автоматическая синхронизация** — изменения в одной вкладке автоматически отражаются во всех остальных
14
+ - **Singleton паттерн** — переиспользование канала для одинаковых имён
15
+ - **Уникальная идентификация** — каждая сессия браузера получает уникальный ID
16
+ - **Типобезопасность** — полная поддержка TypeScript с дженериками
17
+ - **Значения по умолчанию** — поддержка начальных значений и функций-фабрик
18
+ - **Изоляция сессий** — данные не передаются между разными сессиями браузера
19
+
20
+ ## Функция
21
+
22
+ ### `useBroadcastValueRef`
23
+
24
+ Создаёт реактивную переменную, синхронизированную между вкладками через Broadcast Channel.
25
+
26
+ **Параметры:**
27
+ - `name: string` — имя канала для связи между вкладками
28
+ - `defaultValue?: T | string | (() => (T | string))` — значение по умолчанию или функция для его генерации (опционально)
29
+
30
+ **Возвращает:** `Ref<T | string | undefined>` — реактивная переменная Vue, синхронизированная между вкладками
31
+
32
+ ```javascript
33
+ import { useBroadcastValueRef } from '@dxtmisha/functional'
34
+
35
+ // Простое использование
36
+ const sharedCounter = useBroadcastValueRef('counter')
37
+
38
+ // С значением по умолчанию
39
+ const activeTab = useBroadcastValueRef('active-tab', 'home')
40
+
41
+ // С функцией по умолчанию
42
+ const sessionId = useBroadcastValueRef('session', () => Math.random().toString(36))
43
+ ```
44
+
45
+ ## Основное использование
46
+
47
+ ### Базовая синхронизация
48
+
49
+ ```javascript
50
+ import { useBroadcastValueRef } from '@dxtmisha/functional'
51
+
52
+ // Создание синхронизированной переменной
53
+ const counter = useBroadcastValueRef('counter', 0)
54
+
55
+ // В первой вкладке
56
+ counter.value = 5
57
+
58
+ // Во второй вкладке автоматически обновляется
59
+ console.log(counter.value) // 5
60
+
61
+ // Изменение в любой вкладке отражается во всех остальных
62
+ counter.value = 10
63
+ // Все вкладки получат counter.value === 10 мгновенно
64
+ ```
65
+
66
+ ### Singleton паттерн
67
+
68
+ ```javascript
69
+ // При повторном вызове с тем же именем возвращается существующий канал
70
+ const channel1 = useBroadcastValueRef('notifications', null)
71
+ const channel2 = useBroadcastValueRef('notifications', null)
72
+
73
+ console.log(channel1 === channel2) // true - тот же ref
74
+
75
+ channel1.value = { type: 'info', message: 'Привет!' }
76
+ console.log(channel2.value) // { type: 'info', message: 'Привет!' }
77
+ ```
78
+
79
+ ## Использование в компонентах
80
+
81
+ ### Синхронизация уведомлений
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
+ // Уведомление показывается во всех открытых вкладках
113
+ ```
114
+
115
+ ### Общий счётчик
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">Добавить</button>
146
+ // <button @click="removeFromCart">Удалить</button>
147
+ // </div>
148
+ //
149
+ // Счётчик синхронизируется между всеми вкладками
150
+ ```
151
+
152
+ ## Синхронизация состояния
153
+
154
+ ### Активная вкладка
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
+ // Отслеживание изменений из других вкладок
169
+ watch(activeTab, (newTab) => {
170
+ console.log(`Активная вкладка изменена на: ${newTab}`)
171
+ })
172
+
173
+ return {
174
+ activeTab,
175
+ setActiveTab
176
+ }
177
+ }
178
+ }
179
+
180
+ // При переключении вкладки в одном окне,
181
+ // все остальные окна синхронизируются
182
+ ```
183
+
184
+ ### Статус авторизации
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
+ // При входе/выходе в одной вкладке,
214
+ // все остальные вкладки обновляются мгновенно
215
+ ```
216
+
217
+ ## Отличия от useStorageRef
218
+
219
+ ```javascript
220
+ // useStorageRef - через localStorage (постоянное хранение)
221
+ const persistentData = useStorageRef('theme', 'light')
222
+ // - Данные сохраняются в localStorage
223
+ // - Синхронизация через storage event
224
+ // - Более медленная передача данных
225
+ // - Данные остаются после закрытия браузера
226
+
227
+ // useBroadcastValueRef - через Broadcast Channel (в памяти)
228
+ const realtimeData = useBroadcastValueRef('active-users', [])
229
+ // - Данные в памяти (не сохраняются)
230
+ // - Мгновенная синхронизация
231
+ // - Быстрая передача данных
232
+ // - Данные теряются при закрытии всех вкладок
233
+ // - Изоляция между сессиями браузера
234
+ ```
235
+
236
+ ## Работа с типами данных
237
+
238
+ ```javascript
239
+ // Числа
240
+ const counter = useBroadcastValueRef<number>('counter', 0)
241
+ counter.value = 42
242
+
243
+ // Строки
244
+ const message = useBroadcastValueRef<string>('message', '')
245
+ message.value = 'Привет из другой вкладки!'
246
+
247
+ // Объекты
248
+ const userState = useBroadcastValueRef('user-state', {
249
+ id: null,
250
+ name: '',
251
+ online: false
252
+ })
253
+ userState.value = { id: 1, name: 'Иван', online: true }
254
+
255
+ // Массивы
256
+ const activeUsers = useBroadcastValueRef<number[]>('active-users', [])
257
+ activeUsers.value = [1, 2, 3, 4, 5]
258
+ ```
259
+
260
+ ## Примеры использования
261
+
262
+ ### Чат между вкладками
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
+ // Сообщения появляются во всех открытых вкладках мгновенно
278
+ ```
279
+
280
+ ### Синхронизация воспроизведения
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
+ // Управление плеером в одной вкладке
298
+ // влияет на плееры во всех остальных
299
+ ```
300
+
301
+ ### Общие фильтры
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
+ // Изменение фильтров синхронизируется
315
+ // между всеми открытыми страницами поиска
316
+ ```
317
+
318
+ ## Уникальная идентификация сессии
319
+
320
+ Композабл автоматически создаёт уникальный ID для каждой сессии браузера и сохраняет его в localStorage:
321
+
322
+ ```javascript
323
+ // При первом запуске генерируется случайный ID
324
+ // broadcast__name_1234567__counter
325
+ // ^^^^^^^^^ - уникальный ID сессии
326
+
327
+ // Этот ID переиспользуется для всех каналов в рамках одной сессии
328
+ // Разные сессии браузера имеют разные ID и не пересекаются
329
+ ```
330
+
331
+ ## Особенности Broadcast Channel API
332
+
333
+ ```javascript
334
+ // Broadcast Channel работает только:
335
+ // - В рамках одного origin (протокол + домен + порт)
336
+ // - Между вкладками одного браузера
337
+ // - В той же сессии браузера
338
+
339
+ // НЕ работает:
340
+ // - Между разными браузерами
341
+ // - Между разными доменами
342
+ // - В режиме инкогнито между обычными вкладками
343
+ ```
344
+
@@ -0,0 +1,348 @@
1
+ import {Meta} from '@storybook/addon-docs/blocks'
2
+
3
+ <Meta title='@dxtmisha/functional/ru/Composables/useCookieRef'/>
4
+
5
+ # Композабл useCookieRef
6
+
7
+ Композабл для создания реактивной переменной, синхронизированной с cookies браузера. Автоматически управляет чтением и записью значений в cookies с поддержкой синхронизации между вкладками через Broadcast Channel API, настройками срока действия и singleton паттерна для эффективного переиспользования.
8
+
9
+ ## Основные возможности
10
+
11
+ - **Двусторонняя синхронизация** — автоматическая синхронизация между ref и cookies
12
+ - **Синхронизация между вкладками** — изменения в одной вкладке мгновенно отражаются во всех остальных через Broadcast Channel
13
+ - **Автоматическое сохранение** — изменения ref автоматически сохраняются в cookies
14
+ - **Настройка срока действия** — поддержка expires, max-age и других опций cookies
15
+ - **Singleton паттерн** — переиспользование ref для одинаковых имён cookies
16
+ - **Типобезопасность** — полная поддержка TypeScript с дженериками
17
+ - **Значения по умолчанию** — поддержка начальных значений и функций-фабрик
18
+ - **Интеграция с Cookie** — использует класс Cookie для управления cookies
19
+
20
+ ## Функция
21
+
22
+ ### `useCookieRef`
23
+
24
+ Создаёт реактивную переменную, синхронизированную с cookies браузера.
25
+
26
+ **Параметры:**
27
+ - `name: string` — имя cookie
28
+ - `defaultValue?: T | string | (() => (T | string))` — значение по умолчанию или функция для его генерации (опционально)
29
+ - `options?: CookieOptions` — дополнительные параметры cookie (опционально)
30
+
31
+ **Возвращает:** `Ref<T | string | undefined>` — реактивная переменная Vue, связанная с cookie
32
+
33
+ **CookieOptions:**
34
+ - `expires?: number | Date` — дата истечения срока действия
35
+ - `path?: string` — путь для cookie (по умолчанию '/')
36
+ - `domain?: string` — домен для cookie
37
+ - `secure?: boolean` — использовать только HTTPS
38
+ - `sameSite?: 'Strict' | 'Lax' | 'None'` — политика SameSite
39
+
40
+ ```javascript
41
+ import { useCookieRef } from '@dxtmisha/functional'
42
+
43
+ // Простое использование
44
+ const userToken = useCookieRef('token')
45
+
46
+ // С значением по умолчанию
47
+ const theme = useCookieRef('theme', 'light')
48
+
49
+ // С опциями (срок действия 7 дней)
50
+ const rememberMe = useCookieRef('remember', true, {
51
+ expires: 7,
52
+ path: '/',
53
+ sameSite: 'Lax'
54
+ })
55
+ ```
56
+
57
+ ## Основное использование
58
+
59
+ ### Базовая синхронизация
60
+
61
+ ```javascript
62
+ import { useCookieRef } from '@dxtmisha/functional'
63
+
64
+ // Создание ref, синхронизированного с cookie
65
+ const userTheme = useCookieRef('theme', 'light')
66
+
67
+ // При изменении ref - cookie автоматически обновляется
68
+ userTheme.value = 'dark'
69
+ // document.cookie содержит: 'theme=dark'
70
+
71
+ // При перезагрузке страницы значение восстанавливается
72
+ console.log(userTheme.value) // 'dark'
73
+ ```
74
+
75
+ ### Singleton паттерн
76
+
77
+ ```javascript
78
+ // При повторном вызове с тем же именем возвращается существующий ref
79
+ const token1 = useCookieRef('auth-token', '')
80
+ const token2 = useCookieRef('auth-token', '')
81
+
82
+ console.log(token1 === token2) // true - тот же ref
83
+
84
+ token1.value = 'abc123'
85
+ console.log(token2.value) // 'abc123' - оба указывают на один ref
86
+ ```
87
+
88
+ ### Синхронизация между вкладками
89
+
90
+ ```javascript
91
+ import { useCookieRef } from '@dxtmisha/functional'
92
+
93
+ // В первой вкладке
94
+ const userState = useCookieRef('user-online', 'active')
95
+ userState.value = 'away'
96
+
97
+ // Во второй вкладке автоматически обновляется мгновенно
98
+ // userState.value === 'away' (благодаря Broadcast Channel)
99
+
100
+ // В третьей вкладке
101
+ const sameState = useCookieRef('user-online', 'active')
102
+ console.log(sameState.value) // 'away'
103
+
104
+ // Изменение в любой вкладке синхронизируется со всеми остальными
105
+ ```
106
+
107
+ ## Использование в компонентах
108
+
109
+ ### Настройки пользователя
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 год
118
+ })
119
+
120
+ const language = useCookieRef('user-language', 'ru', {
121
+ expires: 365
122
+ })
123
+
124
+ const fontSize = useCookieRef('font-size', 16, {
125
+ expires: 30 // 30 дней
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">Светлая</option>
140
+ // <option value="dark">Тёмная</option>
141
+ // </select>
142
+ // <select v-model="language">
143
+ // <option value="ru">Русский</option>
144
+ // <option value="en">English</option>
145
+ // </select>
146
+ // </div>
147
+ ```
148
+
149
+ ### Авторизация с "Запомнить меня"
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 дней
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 дней или 1 день
166
+ authToken.value = token
167
+
168
+ // Обновить опции cookie
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
192
+
193
+ ### Срок действия
194
+
195
+ ```javascript
196
+ // Expires в днях
197
+ const token = useCookieRef('token', '', {
198
+ expires: 7 // удалится через 7 дней
199
+ })
200
+
201
+ // Expires как Date
202
+ const session = useCookieRef('session', '', {
203
+ expires: new Date('2025-12-31')
204
+ })
205
+
206
+ // Без expires (сессионная cookie, удаляется при закрытии браузера)
207
+ const tempData = useCookieRef('temp', '')
208
+ ```
209
+
210
+ ### Безопасность
211
+
212
+ ```javascript
213
+ // HTTPS only
214
+ const secureToken = useCookieRef('secure-token', '', {
215
+ secure: true,
216
+ sameSite: 'Strict'
217
+ })
218
+
219
+ // Cross-site запросы
220
+ const apiToken = useCookieRef('api-token', '', {
221
+ secure: true,
222
+ sameSite: 'None' // требуется secure: true
223
+ })
224
+ ```
225
+
226
+ ### Путь и домен
227
+
228
+ ```javascript
229
+ // Для конкретного пути
230
+ const adminPrefs = useCookieRef('admin-prefs', {}, {
231
+ path: '/admin'
232
+ })
233
+
234
+ // Для поддомена
235
+ const sharedData = useCookieRef('shared', '', {
236
+ domain: '.example.com' // доступно для всех поддоменов
237
+ })
238
+ ```
239
+
240
+ ## Работа с типами данных
241
+
242
+ ```javascript
243
+ // Строки
244
+ const username = useCookieRef<string>('username', '')
245
+ username.value = 'Иван'
246
+
247
+ // Числа (автоматическая сериализация)
248
+ const count = useCookieRef<number>('count', 0)
249
+ count.value = 42
250
+
251
+ // Булевы значения
252
+ const accepted = useCookieRef<boolean>('accepted', false)
253
+ accepted.value = true
254
+
255
+ // Объекты (JSON сериализация)
256
+ const userPrefs = useCookieRef('prefs', {
257
+ theme: 'dark',
258
+ notifications: true
259
+ })
260
+ userPrefs.value = { theme: 'light', notifications: false }
261
+ ```
262
+
263
+ ## Интеграция с классом Cookie
264
+
265
+ Композабл использует класс `Cookie` для управления cookies:
266
+
267
+ ```javascript
268
+ import { Cookie } from '@dxtmisha/functional'
269
+
270
+ // Прямое использование класса
271
+ const cookie = new Cookie('theme')
272
+ cookie.set('dark', { expires: 30 })
273
+ console.log(cookie.get()) // 'dark'
274
+
275
+ // useCookieRef автоматически синхронизируется
276
+ const themeRef = useCookieRef('theme')
277
+ console.log(themeRef.value) // 'dark'
278
+
279
+ // Изменения через Cookie отражаются в ref (через Broadcast Channel)
280
+ cookie.set('light')
281
+ // themeRef.value автоматически обновится во всех вкладках
282
+ ```
283
+
284
+ ## Примеры использования
285
+
286
+ ### Согласие на cookies (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
+ // В шаблоне:
299
+ // <div v-if="!cookieConsent" class="cookie-banner">
300
+ // <p>Мы используем cookies...</p>
301
+ // <button @click="acceptCookies">Принять</button>
302
+ // </div>
303
+ ```
304
+
305
+ ### Корзина покупок
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
+ // Корзина синхронизируется между вкладками автоматически
321
+ ```
322
+
323
+ ### Отслеживание последних просмотров
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) // Храним последние 10
333
+ }
334
+ ```
335
+
336
+ ## Преимущества синхронизации через Broadcast Channel
337
+
338
+ **В отличие от обычного ref с cookie:**
339
+ - ✅ Мгновенная синхронизация между вкладками (не требуется перезагрузка)
340
+ - ✅ Реальное время обновления (без задержек)
341
+ - ✅ Эффективная передача данных (не через localStorage)
342
+ - ✅ Изоляция сессий браузера
343
+
344
+ **Механизм работы:**
345
+ 1. Изменение в одной вкладке → сохраняется в cookie
346
+ 2. Через Broadcast Channel (`__cookie:${name}`) отправляется сообщение
347
+ 3. Все остальные вкладки получают обновление мгновенно
348
+ 4. ref во всех вкладках обновляется автоматически