@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.
- package/package.json +3 -2
- 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,288 @@
|
|
|
1
|
+
import {Meta} from '@storybook/addon-docs/blocks'
|
|
2
|
+
|
|
3
|
+
<Meta title='@dxtmisha/functional/en/Composables/useGeoIntlRef'/>
|
|
4
|
+
|
|
5
|
+
# Composable useGeoIntlRef
|
|
6
|
+
|
|
7
|
+
Composable for creating a reactive internationalization object. Provides methods for formatting numbers, currencies, dates and names with automatic locale detection. All methods return computed values that automatically update when input data changes.
|
|
8
|
+
|
|
9
|
+
## Key Features
|
|
10
|
+
|
|
11
|
+
- **Number formatting** — localized number representation with separators
|
|
12
|
+
- **Currency formatting** — displaying monetary amounts with currency symbols
|
|
13
|
+
- **Date formatting** — various date and time formats by regional standards
|
|
14
|
+
- **Country and language names** — localized region names
|
|
15
|
+
- **Relative time** — "2 days ago", "in 5 minutes", etc.
|
|
16
|
+
- **Percentages and units** — formatting percentages and measurement units
|
|
17
|
+
- **Reactivity** — all methods return computed that update automatically
|
|
18
|
+
- **Auto locale detection** — uses user geolocation by default
|
|
19
|
+
|
|
20
|
+
## Function
|
|
21
|
+
|
|
22
|
+
### `useGeoIntlRef()`
|
|
23
|
+
|
|
24
|
+
Creates and returns instance of `GeoIntlRef` class for working with data formatting.
|
|
25
|
+
|
|
26
|
+
**Parameters:** none
|
|
27
|
+
|
|
28
|
+
**Returns:** `GeoIntlRef` — object with formatting methods
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
import { useGeoIntlRef } from '@dxtmisha/functional'
|
|
32
|
+
|
|
33
|
+
// Create instance
|
|
34
|
+
const intl = useGeoIntlRef()
|
|
35
|
+
|
|
36
|
+
// All methods return ComputedRef
|
|
37
|
+
const formatted = intl.number(1234.56)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Number Formatting Methods
|
|
41
|
+
|
|
42
|
+
### `number`
|
|
43
|
+
|
|
44
|
+
Format number according to locale.
|
|
45
|
+
|
|
46
|
+
**Parameters:**
|
|
47
|
+
- `value: RefOrNormal<NumberOrString>` — number to format
|
|
48
|
+
- `options?: Intl.NumberFormatOptions` — formatting options
|
|
49
|
+
|
|
50
|
+
**Returns:** `ComputedRef<string>`
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
const intl = useGeoIntlRef()
|
|
54
|
+
const count = ref(1234567.89)
|
|
55
|
+
|
|
56
|
+
intl.number(count) // ComputedRef<'1,234,567.89'>
|
|
57
|
+
intl.number(1000, { minimumFractionDigits: 2 }) // ComputedRef<'1,000.00'>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### `decimal`
|
|
61
|
+
|
|
62
|
+
Returns decimal separator symbol for current locale.
|
|
63
|
+
|
|
64
|
+
**Returns:** `ComputedRef<string>`
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
const intl = useGeoIntlRef()
|
|
68
|
+
intl.decimal() // ComputedRef<'.'> (for en-US)
|
|
69
|
+
// ComputedRef<','> (for ru-RU)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### `percent`
|
|
73
|
+
|
|
74
|
+
Format number as percentage.
|
|
75
|
+
|
|
76
|
+
**Parameters:**
|
|
77
|
+
- `value: RefOrNormal<NumberOrString>` — number to format (0.15 = 15%)
|
|
78
|
+
- `options?: Intl.NumberFormatOptions` — formatting options
|
|
79
|
+
|
|
80
|
+
**Returns:** `ComputedRef<string>`
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
intl.percent(0.15) // ComputedRef<'15%'>
|
|
84
|
+
intl.percent(1.25) // ComputedRef<'125%'>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### `percentBy100`
|
|
88
|
+
|
|
89
|
+
Format number as percentage (value already in percents).
|
|
90
|
+
|
|
91
|
+
**Parameters:**
|
|
92
|
+
- `value: RefOrNormal<NumberOrString>` — number to format (15 = 15%)
|
|
93
|
+
- `options?: Intl.NumberFormatOptions` — formatting options
|
|
94
|
+
|
|
95
|
+
**Returns:** `ComputedRef<string>`
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
intl.percentBy100(15) // ComputedRef<'15%'>
|
|
99
|
+
intl.percentBy100(125) // ComputedRef<'125%'>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Currency Formatting Methods
|
|
103
|
+
|
|
104
|
+
### `currency`
|
|
105
|
+
|
|
106
|
+
Format monetary amount.
|
|
107
|
+
|
|
108
|
+
**Parameters:**
|
|
109
|
+
- `value: RefOrNormal<NumberOrString>` — amount to format
|
|
110
|
+
- `currencyOptions?: RefOrNormal<string | Intl.NumberFormatOptions>` — currency code or options
|
|
111
|
+
- `numberOnly?: boolean` — don't show currency symbol (default `false`)
|
|
112
|
+
|
|
113
|
+
**Returns:** `ComputedRef<string>`
|
|
114
|
+
|
|
115
|
+
```javascript
|
|
116
|
+
const intl = useGeoIntlRef()
|
|
117
|
+
const price = ref(99.99)
|
|
118
|
+
|
|
119
|
+
intl.currency(price, 'USD') // ComputedRef<'$99.99'>
|
|
120
|
+
intl.currency(1234.56, 'RUB') // ComputedRef<'RUB 1,234.56'>
|
|
121
|
+
intl.currency(500, 'EUR', true) // ComputedRef<'500.00'> (no symbol)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### `unit`
|
|
125
|
+
|
|
126
|
+
Format with measurement units.
|
|
127
|
+
|
|
128
|
+
**Parameters:**
|
|
129
|
+
- `value: RefOrNormal<NumberOrString>` — value to format
|
|
130
|
+
- `unitOptions?: string | Intl.NumberFormatOptions` — unit or options
|
|
131
|
+
|
|
132
|
+
**Returns:** `ComputedRef<string>`
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
intl.unit(100, 'kilometer') // ComputedRef<'100 km'>
|
|
136
|
+
intl.unit(50, 'kilogram') // ComputedRef<'50 kg'>
|
|
137
|
+
intl.unit(1.5, 'hour') // ComputedRef<'1.5 hr'>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Date Formatting Methods
|
|
141
|
+
|
|
142
|
+
### `date`
|
|
143
|
+
|
|
144
|
+
Format date and time.
|
|
145
|
+
|
|
146
|
+
**Parameters:**
|
|
147
|
+
- `value: RefOrNormal<NumberOrStringOrDate>` — date to format
|
|
148
|
+
- `type?: GeoDate` — format type ('date' | 'time' | 'datetime' | 'full' | 'long' | 'medium' | 'short')
|
|
149
|
+
- `styleOptions?: Intl.DateTimeFormatOptions['month'] | Intl.DateTimeFormatOptions` — style or options
|
|
150
|
+
- `hour24?: boolean` — use 24-hour format
|
|
151
|
+
|
|
152
|
+
**Returns:** `ComputedRef<string>`
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
const date = ref(new Date('2024-10-15 14:30:00'))
|
|
156
|
+
|
|
157
|
+
intl.date(date) // ComputedRef<'10/15/2024'>
|
|
158
|
+
intl.date(date, 'time') // ComputedRef<'2:30 PM'>
|
|
159
|
+
intl.date(date, 'datetime') // ComputedRef<'10/15/2024, 2:30 PM'>
|
|
160
|
+
intl.date(date, 'full') // ComputedRef<'Tuesday, October 15, 2024'>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### `relative`
|
|
164
|
+
|
|
165
|
+
Format relative time ("2 days ago", "in 1 hour").
|
|
166
|
+
|
|
167
|
+
**Parameters:**
|
|
168
|
+
- `value: RefOrNormal<NumberOrStringOrDate>` — date to compare
|
|
169
|
+
- `styleOptions?: Intl.RelativeTimeFormatStyle | Intl.RelativeTimeFormatOptions` — style ('long' | 'short' | 'narrow')
|
|
170
|
+
- `todayValue?: Date` — current date (default `new Date()`)
|
|
171
|
+
|
|
172
|
+
**Returns:** `ComputedRef<string>`
|
|
173
|
+
|
|
174
|
+
```javascript
|
|
175
|
+
const twoDaysAgo = new Date()
|
|
176
|
+
twoDaysAgo.setDate(twoDaysAgo.getDate() - 2)
|
|
177
|
+
|
|
178
|
+
intl.relative(twoDaysAgo) // ComputedRef<'2 days ago'>
|
|
179
|
+
intl.relative(twoDaysAgo, 'short') // ComputedRef<'2 days ago'>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### `relativeLimit`
|
|
183
|
+
|
|
184
|
+
Format relative time with limit. If difference exceeds limit, outputs absolute date.
|
|
185
|
+
|
|
186
|
+
**Parameters:**
|
|
187
|
+
- `value: RefOrNormal<NumberOrStringOrDate>` — date to format
|
|
188
|
+
- `limit: number` — limit in days
|
|
189
|
+
- `todayValue?: Date` — current date
|
|
190
|
+
- `relativeOptions?: Intl.RelativeTimeFormatStyle | Intl.RelativeTimeFormatOptions` — relative format options
|
|
191
|
+
- `dateOptions?: Intl.DateTimeFormatOptions['month'] | Intl.DateTimeFormatOptions` — date format options
|
|
192
|
+
- `type?: GeoDate` — date format type
|
|
193
|
+
- `hour24?: boolean` — 24-hour format
|
|
194
|
+
|
|
195
|
+
**Returns:** `ComputedRef<string>`
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
const threeDaysAgo = new Date()
|
|
199
|
+
threeDaysAgo.setDate(threeDaysAgo.getDate() - 3)
|
|
200
|
+
|
|
201
|
+
intl.relativeLimit(threeDaysAgo, 7) // ComputedRef<'3 days ago'>
|
|
202
|
+
|
|
203
|
+
const tenDaysAgo = new Date()
|
|
204
|
+
tenDaysAgo.setDate(tenDaysAgo.getDate() - 10)
|
|
205
|
+
|
|
206
|
+
intl.relativeLimit(tenDaysAgo, 7) // ComputedRef<'10/05/2024'> (absolute date)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Name Methods
|
|
210
|
+
|
|
211
|
+
### `display`
|
|
212
|
+
|
|
213
|
+
Get localized name (language, region, script, etc.).
|
|
214
|
+
|
|
215
|
+
**Parameters:**
|
|
216
|
+
- `value?: RefOrNormal<string>` — code to get name for
|
|
217
|
+
- `typeOptions?: Intl.DisplayNamesOptions['type'] | Intl.DisplayNamesOptions` — type ('language' | 'region' | 'script' | 'currency')
|
|
218
|
+
|
|
219
|
+
**Returns:** `ComputedRef<string>`
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
intl.display('US', 'region') // ComputedRef<'United States'>
|
|
223
|
+
intl.display('en', 'language') // ComputedRef<'English'>
|
|
224
|
+
intl.display('USD', 'currency') // ComputedRef<'US Dollar'>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### `languageName`
|
|
228
|
+
|
|
229
|
+
Get language name.
|
|
230
|
+
|
|
231
|
+
**Parameters:**
|
|
232
|
+
- `value?: RefOrNormal<string>` — language code
|
|
233
|
+
- `style?: Intl.RelativeTimeFormatStyle` — display style
|
|
234
|
+
|
|
235
|
+
**Returns:** `ComputedRef<string>`
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
intl.languageName('en') // ComputedRef<'English'>
|
|
239
|
+
intl.languageName('de') // ComputedRef<'German'>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### `countryName`
|
|
243
|
+
|
|
244
|
+
Get country name.
|
|
245
|
+
|
|
246
|
+
**Parameters:**
|
|
247
|
+
- `value?: RefOrNormal<string>` — country code
|
|
248
|
+
- `style?: Intl.RelativeTimeFormatStyle` — display style
|
|
249
|
+
|
|
250
|
+
**Returns:** `ComputedRef<string>`
|
|
251
|
+
|
|
252
|
+
```javascript
|
|
253
|
+
intl.countryName('US') // ComputedRef<'United States'>
|
|
254
|
+
intl.countryName('GB') // ComputedRef<'United Kingdom'>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Usage Examples
|
|
258
|
+
|
|
259
|
+
### Basic Usage
|
|
260
|
+
|
|
261
|
+
```javascript
|
|
262
|
+
import { ref } from 'vue'
|
|
263
|
+
import { useGeoIntlRef } from '@dxtmisha/functional'
|
|
264
|
+
|
|
265
|
+
const intl = useGeoIntlRef()
|
|
266
|
+
const price = ref(1234.56)
|
|
267
|
+
|
|
268
|
+
// All methods return ComputedRef
|
|
269
|
+
const formatted = intl.currency(price, 'USD')
|
|
270
|
+
console.log(formatted.value) // '$1,234.56'
|
|
271
|
+
|
|
272
|
+
// Automatic update on change
|
|
273
|
+
price.value = 2000
|
|
274
|
+
console.log(formatted.value) // '$2,000.00'
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Reactive Parameters
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
const price = ref(100)
|
|
281
|
+
const currency = ref('USD')
|
|
282
|
+
|
|
283
|
+
// Reactive currency and value
|
|
284
|
+
const formatted = intl.currency(price, currency)
|
|
285
|
+
|
|
286
|
+
currency.value = 'EUR'
|
|
287
|
+
// formatted.value automatically updates
|
|
288
|
+
```
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import {Meta} from '@storybook/addon-docs/blocks'
|
|
2
|
+
|
|
3
|
+
<Meta title='@dxtmisha/functional/en/Composables/useHashRef'/>
|
|
4
|
+
|
|
5
|
+
# Composable useHashRef
|
|
6
|
+
|
|
7
|
+
Composable for creating a reactive variable synchronized with URL hash. Automatically manages reading and writing values to the hash part of the URL, providing two-way synchronization between Vue reactive state and browser address bar parameters.
|
|
8
|
+
|
|
9
|
+
## Key Features
|
|
10
|
+
|
|
11
|
+
- **Two-way synchronization** — automatic sync between ref and URL hash
|
|
12
|
+
- **Automatic persistence** — ref changes automatically reflect in URL
|
|
13
|
+
- **Reactivity** — URL hash changes automatically update ref
|
|
14
|
+
- **Instance caching** — reuses ref for same parameter names
|
|
15
|
+
- **Type safety** — full TypeScript support with generics
|
|
16
|
+
- **Default values** — supports initial values and factory functions
|
|
17
|
+
- **Auto initialization** — loads existing values from URL on creation
|
|
18
|
+
|
|
19
|
+
## Function
|
|
20
|
+
|
|
21
|
+
### `useHashRef`
|
|
22
|
+
|
|
23
|
+
Creates a reactive variable synchronized with a parameter in URL hash.
|
|
24
|
+
|
|
25
|
+
**Parameters:**
|
|
26
|
+
- `name: string` — parameter name in hash URL
|
|
27
|
+
- `defaultValue?: T | (() => T)` — default value or function to generate it (optional)
|
|
28
|
+
|
|
29
|
+
**Returns:** `Ref<T>` — Vue reactive variable linked to hash parameter
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
import { useHashRef } from '@dxtmisha/functional'
|
|
33
|
+
|
|
34
|
+
// Simple usage without default value
|
|
35
|
+
const currentTab = useHashRef('tab')
|
|
36
|
+
console.log(currentTab.value) // undefined (if hash is empty)
|
|
37
|
+
|
|
38
|
+
// With default value
|
|
39
|
+
const activeView = useHashRef('view', 'grid')
|
|
40
|
+
console.log(activeView.value) // 'grid' (if hash doesn't contain 'view')
|
|
41
|
+
|
|
42
|
+
// With factory function for default value
|
|
43
|
+
const userId = useHashRef('userId', () => Math.floor(Math.random() * 1000))
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Basic Usage
|
|
47
|
+
|
|
48
|
+
### Basic Synchronization
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
import { useHashRef } from '@dxtmisha/functional'
|
|
52
|
+
|
|
53
|
+
// Create ref synchronized with hash
|
|
54
|
+
const currentPage = useHashRef('page', 1)
|
|
55
|
+
|
|
56
|
+
// When ref changes - URL automatically updates
|
|
57
|
+
currentPage.value = 2
|
|
58
|
+
// URL: #page=2
|
|
59
|
+
|
|
60
|
+
currentPage.value = 5
|
|
61
|
+
// URL: #page=5
|
|
62
|
+
|
|
63
|
+
// When URL changes manually - ref automatically updates
|
|
64
|
+
// User changes URL to: #page=10
|
|
65
|
+
console.log(currentPage.value) // 10
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Working with Multiple Parameters
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
// Multiple independent hash parameters
|
|
72
|
+
const searchQuery = useHashRef('q', '')
|
|
73
|
+
const sortOrder = useHashRef('sort', 'asc')
|
|
74
|
+
const pageNumber = useHashRef('page', 1)
|
|
75
|
+
|
|
76
|
+
searchQuery.value = 'vue composables'
|
|
77
|
+
sortOrder.value = 'desc'
|
|
78
|
+
pageNumber.value = 3
|
|
79
|
+
|
|
80
|
+
// URL: #q=vue composables;sort=desc;page=3
|
|
81
|
+
|
|
82
|
+
// All parameters stay synchronized
|
|
83
|
+
console.log(searchQuery.value) // 'vue composables'
|
|
84
|
+
console.log(sortOrder.value) // 'desc'
|
|
85
|
+
console.log(pageNumber.value) // 3
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Instance Caching
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
// Repeated calls with same name return existing ref
|
|
92
|
+
const tab1 = useHashRef('activeTab', 'home')
|
|
93
|
+
const tab2 = useHashRef('activeTab', 'profile')
|
|
94
|
+
|
|
95
|
+
console.log(tab1 === tab2) // true - same ref
|
|
96
|
+
|
|
97
|
+
tab1.value = 'settings'
|
|
98
|
+
console.log(tab2.value) // 'settings' - both point to same ref
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Usage in Components
|
|
102
|
+
|
|
103
|
+
### Tab Management
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
import { useHashRef } from '@dxtmisha/functional'
|
|
107
|
+
|
|
108
|
+
export default {
|
|
109
|
+
setup() {
|
|
110
|
+
const activeTab = useHashRef('tab', 'overview')
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
activeTab,
|
|
114
|
+
tabs: ['overview', 'details', 'settings']
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Template:
|
|
120
|
+
// <div>
|
|
121
|
+
// <button
|
|
122
|
+
// v-for="tab in tabs"
|
|
123
|
+
// :key="tab"
|
|
124
|
+
// @click="activeTab = tab"
|
|
125
|
+
// :class="{ active: activeTab === tab }"
|
|
126
|
+
// >
|
|
127
|
+
// {{ tab }}
|
|
128
|
+
// </button>
|
|
129
|
+
// </div>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Pagination with State Persistence
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
import { computed } from 'vue'
|
|
136
|
+
import { useHashRef } from '@dxtmisha/functional'
|
|
137
|
+
|
|
138
|
+
export default {
|
|
139
|
+
setup() {
|
|
140
|
+
const currentPage = useHashRef('page', 1)
|
|
141
|
+
const itemsPerPage = 20
|
|
142
|
+
const totalItems = 100
|
|
143
|
+
|
|
144
|
+
const totalPages = computed(() =>
|
|
145
|
+
Math.ceil(totalItems / itemsPerPage)
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
const nextPage = () => {
|
|
149
|
+
if (currentPage.value < totalPages.value) {
|
|
150
|
+
currentPage.value++
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const prevPage = () => {
|
|
155
|
+
if (currentPage.value > 1) {
|
|
156
|
+
currentPage.value--
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
currentPage,
|
|
162
|
+
totalPages,
|
|
163
|
+
nextPage,
|
|
164
|
+
prevPage
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Filters and Search
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
import { watch } from 'vue'
|
|
174
|
+
import { useHashRef } from '@dxtmisha/functional'
|
|
175
|
+
|
|
176
|
+
export default {
|
|
177
|
+
setup() {
|
|
178
|
+
const searchQuery = useHashRef('q', '')
|
|
179
|
+
const category = useHashRef('category', 'all')
|
|
180
|
+
const sortBy = useHashRef('sort', 'date')
|
|
181
|
+
|
|
182
|
+
// Watch changes to load data
|
|
183
|
+
watch([searchQuery, category, sortBy], () => {
|
|
184
|
+
console.log('Loading data with params:')
|
|
185
|
+
console.log('Search:', searchQuery.value)
|
|
186
|
+
console.log('Category:', category.value)
|
|
187
|
+
console.log('Sort:', sortBy.value)
|
|
188
|
+
// API request here
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
searchQuery,
|
|
193
|
+
category,
|
|
194
|
+
sortBy
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Working with Data Types
|
|
201
|
+
|
|
202
|
+
### Numeric Values
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
// Hash automatically converts types
|
|
206
|
+
const pageNumber = useHashRef<number>('page', 1)
|
|
207
|
+
|
|
208
|
+
pageNumber.value = 5
|
|
209
|
+
// URL: #page=5
|
|
210
|
+
|
|
211
|
+
// When loading from URL, string is automatically converted
|
|
212
|
+
// URL: #page=10
|
|
213
|
+
console.log(typeof pageNumber.value) // 'number'
|
|
214
|
+
console.log(pageNumber.value) // 10
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Boolean Values
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
const isActive = useHashRef<boolean>('active', false)
|
|
221
|
+
|
|
222
|
+
isActive.value = true
|
|
223
|
+
// URL: #active=true
|
|
224
|
+
|
|
225
|
+
// Automatic conversion from string
|
|
226
|
+
// URL: #active=false
|
|
227
|
+
console.log(typeof isActive.value) // 'boolean'
|
|
228
|
+
console.log(isActive.value) // false
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Objects and Arrays
|
|
232
|
+
|
|
233
|
+
```javascript
|
|
234
|
+
// For complex types, serialization is used
|
|
235
|
+
const filters = useHashRef('filters', { min: 0, max: 100 })
|
|
236
|
+
|
|
237
|
+
filters.value = { min: 20, max: 80 }
|
|
238
|
+
// URL will contain serialized representation
|
|
239
|
+
|
|
240
|
+
// Values are restored on load
|
|
241
|
+
console.log(filters.value) // { min: 20, max: 80 }
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Integration with Hash Class
|
|
245
|
+
|
|
246
|
+
The composable uses `Hash` class to manage URL hash:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
import { Hash } from '@dxtmisha/functional'
|
|
250
|
+
|
|
251
|
+
// Direct class usage
|
|
252
|
+
Hash.set('tab', 'profile')
|
|
253
|
+
console.log(Hash.get('tab')) // 'profile'
|
|
254
|
+
|
|
255
|
+
// useHashRef automatically syncs with Hash
|
|
256
|
+
const tab = useHashRef('tab')
|
|
257
|
+
console.log(tab.value) // 'profile'
|
|
258
|
+
|
|
259
|
+
// Changes via Hash reflect in ref
|
|
260
|
+
Hash.set('tab', 'settings')
|
|
261
|
+
console.log(tab.value) // 'settings'
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Usage Examples
|
|
265
|
+
|
|
266
|
+
### Form State Persistence
|
|
267
|
+
|
|
268
|
+
```javascript
|
|
269
|
+
const formData = {
|
|
270
|
+
name: useHashRef('name', ''),
|
|
271
|
+
email: useHashRef('email', ''),
|
|
272
|
+
role: useHashRef('role', 'user')
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// When filling form, state is saved to URL
|
|
276
|
+
formData.name.value = 'John'
|
|
277
|
+
formData.email.value = 'john@example.com'
|
|
278
|
+
formData.role.value = 'admin'
|
|
279
|
+
|
|
280
|
+
// URL: #name=John;email=john@example.com;role=admin
|
|
281
|
+
|
|
282
|
+
// On page reload, state is restored
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Modal Window Management
|
|
286
|
+
|
|
287
|
+
```javascript
|
|
288
|
+
const modalOpen = useHashRef('modal', null)
|
|
289
|
+
|
|
290
|
+
const openModal = (modalName) => {
|
|
291
|
+
modalOpen.value = modalName
|
|
292
|
+
// URL: #modal=confirmation
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const closeModal = () => {
|
|
296
|
+
modalOpen.value = null
|
|
297
|
+
// URL: #modal=null or hash is cleared
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Browser "Back" button closes modal
|
|
301
|
+
```
|
|
302
|
+
|