@dxtmisha/functional-basic 0.1.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.
- package/LICENSE +21 -0
- package/README.md +829 -0
- package/dist/classes/Api.d.ts +161 -0
- package/dist/classes/ApiDefault.d.ts +43 -0
- package/dist/classes/ApiHeaders.d.ts +23 -0
- package/dist/classes/ApiPreparation.d.ts +56 -0
- package/dist/classes/ApiResponse.d.ts +111 -0
- package/dist/classes/ApiStatus.d.ts +89 -0
- package/dist/classes/BroadcastMessage.d.ts +36 -0
- package/dist/classes/Cache.d.ts +34 -0
- package/dist/classes/CacheItem.d.ts +55 -0
- package/dist/classes/CacheStatic.d.ts +17 -0
- package/dist/classes/Cookie.d.ts +58 -0
- package/dist/classes/CookieBlock.d.ts +22 -0
- package/dist/classes/DataStorage.d.ts +82 -0
- package/dist/classes/Datetime.d.ts +482 -0
- package/dist/classes/EventItem.d.ts +160 -0
- package/dist/classes/Geo.d.ts +168 -0
- package/dist/classes/GeoFlag.d.ts +79 -0
- package/dist/classes/GeoIntl.d.ts +262 -0
- package/dist/classes/GeoPhone.d.ts +107 -0
- package/dist/classes/Global.d.ts +21 -0
- package/dist/classes/Hash.d.ts +59 -0
- package/dist/classes/Icons.d.ts +90 -0
- package/dist/classes/Loading.d.ts +49 -0
- package/dist/classes/Meta.d.ts +168 -0
- package/dist/classes/MetaManager.d.ts +103 -0
- package/dist/classes/MetaOg.d.ts +101 -0
- package/dist/classes/MetaTwitter.d.ts +101 -0
- package/dist/classes/ScrollbarWidth.d.ts +33 -0
- package/dist/classes/Translate.d.ts +116 -0
- package/dist/classes/__tests__/Api.test.d.ts +4 -0
- package/dist/classes/__tests__/ApiDefault.test.d.ts +1 -0
- package/dist/classes/__tests__/ApiHeaders.test.d.ts +1 -0
- package/dist/classes/__tests__/ApiPreparation.test.d.ts +1 -0
- package/dist/classes/__tests__/ApiResponse.test.d.ts +4 -0
- package/dist/classes/__tests__/ApiStatus.test.d.ts +1 -0
- package/dist/classes/__tests__/Meta.test.d.ts +4 -0
- package/dist/classes/__tests__/MetaManager.test.d.ts +4 -0
- package/dist/classes/__tests__/MetaOg.test.d.ts +4 -0
- package/dist/classes/__tests__/MetaTwitter.test.d.ts +4 -0
- package/dist/functions/anyToString.d.ts +7 -0
- package/dist/functions/applyTemplate.d.ts +10 -0
- package/dist/functions/arrFill.d.ts +8 -0
- package/dist/functions/copyObject.d.ts +8 -0
- package/dist/functions/createElement.d.ts +13 -0
- package/dist/functions/domQuerySelector.d.ts +7 -0
- package/dist/functions/domQuerySelectorAll.d.ts +7 -0
- package/dist/functions/encodeAttribute.d.ts +7 -0
- package/dist/functions/eventStopPropagation.d.ts +7 -0
- package/dist/functions/executeFunction.d.ts +8 -0
- package/dist/functions/executePromise.d.ts +7 -0
- package/dist/functions/forEach.d.ts +11 -0
- package/dist/functions/frame.d.ts +16 -0
- package/dist/functions/getAttributes.d.ts +8 -0
- package/dist/functions/getClipboardData.d.ts +11 -0
- package/dist/functions/getColumn.d.ts +10 -0
- package/dist/functions/getElement.d.ts +8 -0
- package/dist/functions/getElementId.d.ts +9 -0
- package/dist/functions/getElementItem.d.ts +11 -0
- package/dist/functions/getElementOrWindow.d.ts +8 -0
- package/dist/functions/getExp.d.ts +13 -0
- package/dist/functions/getItemByPath.d.ts +8 -0
- package/dist/functions/getKey.d.ts +7 -0
- package/dist/functions/getLengthOfAllArray.d.ts +8 -0
- package/dist/functions/getMaxLengthAllArray.d.ts +8 -0
- package/dist/functions/getMinLengthAllArray.d.ts +8 -0
- package/dist/functions/getMouseClient.d.ts +8 -0
- package/dist/functions/getMouseClientX.d.ts +7 -0
- package/dist/functions/getMouseClientY.d.ts +7 -0
- package/dist/functions/getObjectByKeys.d.ts +8 -0
- package/dist/functions/getObjectNoUndefined.d.ts +8 -0
- package/dist/functions/getObjectOrNone.d.ts +7 -0
- package/dist/functions/getRandomText.d.ts +11 -0
- package/dist/functions/getRequestString.d.ts +9 -0
- package/dist/functions/getStepPercent.d.ts +8 -0
- package/dist/functions/getStepValue.d.ts +8 -0
- package/dist/functions/goScroll.d.ts +10 -0
- package/dist/functions/inArray.d.ts +8 -0
- package/dist/functions/initScrollbarOffset.d.ts +6 -0
- package/dist/functions/intersectKey.d.ts +8 -0
- package/dist/functions/isArray.d.ts +7 -0
- package/dist/functions/isDifferent.d.ts +9 -0
- package/dist/functions/isDomRuntime.d.ts +10 -0
- package/dist/functions/isFilled.d.ts +9 -0
- package/dist/functions/isFloat.d.ts +7 -0
- package/dist/functions/isFunction.d.ts +8 -0
- package/dist/functions/isInDom.d.ts +8 -0
- package/dist/functions/isIntegerBetween.d.ts +8 -0
- package/dist/functions/isNull.d.ts +8 -0
- package/dist/functions/isNumber.d.ts +7 -0
- package/dist/functions/isObject.d.ts +7 -0
- package/dist/functions/isObjectNotArray.d.ts +7 -0
- package/dist/functions/isSelected.d.ts +8 -0
- package/dist/functions/isSelectedByList.d.ts +8 -0
- package/dist/functions/isString.d.ts +7 -0
- package/dist/functions/isWindow.d.ts +7 -0
- package/dist/functions/random.d.ts +8 -0
- package/dist/functions/replaceRecursive.d.ts +10 -0
- package/dist/functions/replaceTemplate.d.ts +9 -0
- package/dist/functions/secondToTime.d.ts +7 -0
- package/dist/functions/setElementItem.d.ts +10 -0
- package/dist/functions/setValues.d.ts +17 -0
- package/dist/functions/splice.d.ts +12 -0
- package/dist/functions/strFill.d.ts +8 -0
- package/dist/functions/toArray.d.ts +17 -0
- package/dist/functions/toCamelCase.d.ts +7 -0
- package/dist/functions/toCamelCaseFirst.d.ts +7 -0
- package/dist/functions/toDate.d.ts +7 -0
- package/dist/functions/toKebabCase.d.ts +15 -0
- package/dist/functions/toNumber.d.ts +8 -0
- package/dist/functions/toNumberByMax.d.ts +9 -0
- package/dist/functions/toPercent.d.ts +8 -0
- package/dist/functions/toPercentBy100.d.ts +8 -0
- package/dist/functions/transformation.d.ts +21 -0
- package/dist/functions/uniqueArray.d.ts +7 -0
- package/dist/functions/writeClipboardData.d.ts +7 -0
- package/dist/library.d.ts +109 -0
- package/dist/library.js +4916 -0
- package/dist/types/apiTypes.d.ts +116 -0
- package/dist/types/basicTypes.d.ts +126 -0
- package/dist/types/geoTypes.d.ts +86 -0
- package/dist/types/metaTypes.d.ts +584 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,829 @@
|
|
|
1
|
+
# @dxtmisha/functional-basic
|
|
2
|
+
|
|
3
|
+
> Core functional utility library for modern web development without framework dependencies
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@dxtmisha/functional-basic)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://nodejs.org/)
|
|
8
|
+
|
|
9
|
+
A lightweight, framework-agnostic utility library providing essential classes, functions, and types for modern JavaScript/TypeScript development. This is the core package without Vue dependencies, making it perfect for any JavaScript project.
|
|
10
|
+
|
|
11
|
+
## 📦 Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @dxtmisha/functional-basic
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
yarn add @dxtmisha/functional-basic
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @dxtmisha/functional-basic
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## ✨ Features
|
|
26
|
+
|
|
27
|
+
- 🏗️ **30+ Utility Classes** — API, Cache, Geo, Datetime, Hash, Cookie, Meta, Icons, and more
|
|
28
|
+
- 🔧 **90+ Helper Functions** — for arrays, objects, strings, DOM, validation, and math
|
|
29
|
+
- 📝 **TypeScript Types** — full type coverage for all utilities
|
|
30
|
+
- ⚡ **Tree-shakeable** — import only what you use
|
|
31
|
+
- 🌍 **Geolocation & i18n** — auto-detect country, language, and formatting
|
|
32
|
+
- 🎨 **Country Flags** — 200+ flags with localized country names
|
|
33
|
+
- 📞 **Phone Masks** — international codes and number formatting
|
|
34
|
+
- 💾 **Caching** — intelligent cache system with invalidation
|
|
35
|
+
- 🌐 **HTTP Client** — extended API class with caching and loading indicators
|
|
36
|
+
- 🚫 **No Framework Dependencies** — works with vanilla JS, React, Vue, or any framework
|
|
37
|
+
- 📦 **Zero Dependencies** — no external runtime dependencies
|
|
38
|
+
|
|
39
|
+
## 🚀 Quick Start
|
|
40
|
+
|
|
41
|
+
### Utility Functions
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import {
|
|
45
|
+
toArray,
|
|
46
|
+
forEach,
|
|
47
|
+
toCamelCase,
|
|
48
|
+
toKebabCase,
|
|
49
|
+
transformation,
|
|
50
|
+
copyObject,
|
|
51
|
+
isFilled
|
|
52
|
+
} from '@dxtmisha/functional-basic'
|
|
53
|
+
|
|
54
|
+
// Array operations
|
|
55
|
+
const items = toArray('single') // ['single']
|
|
56
|
+
forEach([1, 2, 3], (item) => console.log(item))
|
|
57
|
+
|
|
58
|
+
// String case transformations
|
|
59
|
+
const camel = toCamelCase('my-variable-name') // 'myVariableName'
|
|
60
|
+
const kebab = toKebabCase('myVariableName') // 'my-variable-name'
|
|
61
|
+
|
|
62
|
+
// Object operations
|
|
63
|
+
const data = transformation('{"name":"test"}') // { name: 'test' }
|
|
64
|
+
const copy = copyObject({ deep: { object: true } })
|
|
65
|
+
|
|
66
|
+
// Validation
|
|
67
|
+
if (isFilled(value)) {
|
|
68
|
+
// value is not empty
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Utility Classes
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import {
|
|
76
|
+
Cache,
|
|
77
|
+
Datetime,
|
|
78
|
+
Geo,
|
|
79
|
+
Hash,
|
|
80
|
+
Api,
|
|
81
|
+
Cookie,
|
|
82
|
+
DataStorage
|
|
83
|
+
} from '@dxtmisha/functional-basic'
|
|
84
|
+
|
|
85
|
+
// Caching
|
|
86
|
+
const cache = new Cache()
|
|
87
|
+
const userData = cache.get('user', () => fetchUser())
|
|
88
|
+
|
|
89
|
+
// Date operations
|
|
90
|
+
const datetime = new Datetime('2024-10-15')
|
|
91
|
+
const formatted = datetime.format('YYYY-MM-DD HH:mm')
|
|
92
|
+
|
|
93
|
+
// Geolocation
|
|
94
|
+
const country = Geo.getCountry() // 'US'
|
|
95
|
+
const language = Geo.getLanguage() // 'en'
|
|
96
|
+
|
|
97
|
+
// URL Hash management
|
|
98
|
+
Hash.set('userId', '12345')
|
|
99
|
+
const userId = Hash.get('userId')
|
|
100
|
+
|
|
101
|
+
// HTTP requests
|
|
102
|
+
Api.setUrl('/api/')
|
|
103
|
+
const users = await Api.get({ path: 'users' })
|
|
104
|
+
|
|
105
|
+
// Cookie management
|
|
106
|
+
const theme = new Cookie('theme')
|
|
107
|
+
theme.set('dark')
|
|
108
|
+
|
|
109
|
+
// LocalStorage wrapper
|
|
110
|
+
const settings = new DataStorage('app-settings')
|
|
111
|
+
settings.set({ theme: 'dark' })
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 📚 API Documentation
|
|
115
|
+
|
|
116
|
+
### 🏗️ Classes
|
|
117
|
+
|
|
118
|
+
#### API & Network
|
|
119
|
+
|
|
120
|
+
**Api** — Static HTTP client with caching, loading indicators, and locale support
|
|
121
|
+
```typescript
|
|
122
|
+
Api.setUrl('/api/v1')
|
|
123
|
+
Api.setHeaders({ Authorization: 'Bearer token' })
|
|
124
|
+
|
|
125
|
+
const data = await Api.get({ path: 'users' })
|
|
126
|
+
const result = await Api.post({ path: 'users', request: { name: 'John' } })
|
|
127
|
+
const updated = await Api.put({ path: 'users/123', request: { name: 'Jane' } })
|
|
128
|
+
await Api.delete({ path: 'users/123' })
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**ApiDefault** — Manage default request data
|
|
132
|
+
```typescript
|
|
133
|
+
const apiDefault = new ApiDefault()
|
|
134
|
+
apiDefault.set({ apiKey: 'key-123', version: '1.0' })
|
|
135
|
+
const data = apiDefault.get({ userId: '456' }) // merges with defaults
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**ApiHeaders** — HTTP headers management
|
|
139
|
+
```typescript
|
|
140
|
+
const headers = new ApiHeaders()
|
|
141
|
+
headers.set({ Authorization: 'Bearer token', 'X-Api-Key': 'key-123' })
|
|
142
|
+
const requestHeaders = headers.get({ 'Content-Type': 'application/json' })
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**ApiPreparation** — Request lifecycle hooks
|
|
146
|
+
```typescript
|
|
147
|
+
const preparation = new ApiPreparation()
|
|
148
|
+
preparation.set(async () => {
|
|
149
|
+
// Called before request
|
|
150
|
+
await refreshToken()
|
|
151
|
+
})
|
|
152
|
+
preparation.setEnd(async (response) => {
|
|
153
|
+
// Called after request
|
|
154
|
+
return { reset: false, data: null }
|
|
155
|
+
})
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**ApiResponse** — Response caching and management
|
|
159
|
+
```typescript
|
|
160
|
+
const response = new ApiResponse(apiDefault)
|
|
161
|
+
response.add({ path: '/users', method: 'GET', response: userData })
|
|
162
|
+
const cached = response.get('/users', 'GET')
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**ApiStatus** — Track API request status
|
|
166
|
+
```typescript
|
|
167
|
+
const status = new ApiStatus()
|
|
168
|
+
status.setStatus(200, 'OK')
|
|
169
|
+
status.setError('Network error')
|
|
170
|
+
console.log(status.getStatus()) // 200
|
|
171
|
+
console.log(status.getError()) // 'Network error'
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Loading** — Global loading indicator management
|
|
175
|
+
```typescript
|
|
176
|
+
Loading.show() // show loading
|
|
177
|
+
Loading.hide() // hide loading
|
|
178
|
+
if (Loading.is()) { /* is loading */ }
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### Caching
|
|
182
|
+
|
|
183
|
+
**Cache** — Manage multiple caches with automatic invalidation
|
|
184
|
+
```typescript
|
|
185
|
+
const cache = new Cache()
|
|
186
|
+
const data = cache.get('key', () => expensiveOperation(), [dep1, dep2])
|
|
187
|
+
const asyncData = await cache.getAsync('key', async () => await fetch())
|
|
188
|
+
cache.clear() // clear all caches
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**CacheItem** — Low-level single cache value management
|
|
192
|
+
```typescript
|
|
193
|
+
const item = new CacheItem(() => computeValue())
|
|
194
|
+
const value = item.getCache([dependencies])
|
|
195
|
+
item.clearCache()
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**CacheStatic** — Static cache for global data
|
|
199
|
+
```typescript
|
|
200
|
+
CacheStatic.set('config', configuration)
|
|
201
|
+
const config = CacheStatic.get('config')
|
|
202
|
+
CacheStatic.clear('config')
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### Data Storage
|
|
206
|
+
|
|
207
|
+
**Cookie** — Cookie management with automatic serialization
|
|
208
|
+
```typescript
|
|
209
|
+
const theme = new Cookie('theme')
|
|
210
|
+
theme.set('dark', { age: 365 * 24 * 60 * 60 }) // 1 year
|
|
211
|
+
const value = theme.get('light') // with default
|
|
212
|
+
theme.remove()
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**CookieBlock** — Cookie consent management
|
|
216
|
+
```typescript
|
|
217
|
+
CookieBlock.set(true) // allow cookies
|
|
218
|
+
if (CookieBlock.isBlock()) { /* cookies blocked */ }
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**DataStorage** — LocalStorage/SessionStorage wrapper
|
|
222
|
+
```typescript
|
|
223
|
+
const storage = new DataStorage('settings')
|
|
224
|
+
storage.set({ theme: 'dark', lang: 'en' })
|
|
225
|
+
const settings = storage.get({ theme: 'light' }) // with default
|
|
226
|
+
storage.clear()
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Hash** — URL hash parameter management
|
|
230
|
+
```typescript
|
|
231
|
+
Hash.set('page', 'home')
|
|
232
|
+
Hash.set('userId', '123')
|
|
233
|
+
const page = Hash.get('page', 'home')
|
|
234
|
+
Hash.addWatch('theme', (theme) => applyTheme(theme))
|
|
235
|
+
Hash.go({ page: 'products', category: 'electronics' })
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Global** — Global configuration storage
|
|
239
|
+
```typescript
|
|
240
|
+
Global.add({ apiUrl: 'https://api.example.com', version: '1.0' })
|
|
241
|
+
const apiUrl = Global.get('apiUrl')
|
|
242
|
+
Global.set('apiUrl', 'https://new-api.example.com')
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### Geolocation & Internationalization
|
|
246
|
+
|
|
247
|
+
**Geo** — Location detection and geographic data
|
|
248
|
+
```typescript
|
|
249
|
+
const country = Geo.getCountry() // 'US'
|
|
250
|
+
const language = Geo.getLanguage() // 'en'
|
|
251
|
+
const standard = Geo.getStandard() // 'en-US'
|
|
252
|
+
Geo.set('en-US') // set locale
|
|
253
|
+
const info = Geo.get() // full geo information
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**GeoIntl** — Locale-aware formatting (Intl API)
|
|
257
|
+
```typescript
|
|
258
|
+
const intl = new GeoIntl('en-US')
|
|
259
|
+
const formatted = intl.number(1234.56) // '1,234.56'
|
|
260
|
+
const currency = intl.currency(99.99, 'USD') // '$99.99'
|
|
261
|
+
const date = intl.date(new Date(), 'short') // '10/15/2024'
|
|
262
|
+
const relative = intl.relative(-2, 'day') // '2 days ago'
|
|
263
|
+
const percent = intl.percent(0.75) // '75%'
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**GeoFlag** — Country flags management
|
|
267
|
+
```typescript
|
|
268
|
+
const flag = new GeoFlag('en-US')
|
|
269
|
+
const usa = flag.get('US')
|
|
270
|
+
// { country: 'United States', icon: '@flag-us', value: 'US', ... }
|
|
271
|
+
const list = flag.getList() // list of all countries
|
|
272
|
+
const filtered = flag.getListByFilter('united') // search countries
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**GeoPhone** — Phone codes and masks
|
|
276
|
+
```typescript
|
|
277
|
+
const phone = GeoPhone.get('US')
|
|
278
|
+
// { phone: 1, within: 1, mask: ['+1 (***) ***-****'], value: 'US' }
|
|
279
|
+
const info = GeoPhone.getByPhone('+14155551234')
|
|
280
|
+
// detect country by phone number
|
|
281
|
+
const list = GeoPhone.getList() // all phone codes
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### Date & Time
|
|
285
|
+
|
|
286
|
+
**Datetime** — Date operations and formatting
|
|
287
|
+
```typescript
|
|
288
|
+
const dt = new Datetime('2024-10-15', 'full', 'en-US')
|
|
289
|
+
const formatted = dt.format('YYYY-MM-DD HH:mm:ss')
|
|
290
|
+
const iso = dt.toIso() // ISO 8601 format
|
|
291
|
+
dt.moveByDay(5) // +5 days
|
|
292
|
+
dt.moveByMonth(-1) // -1 month
|
|
293
|
+
const year = dt.getYear()
|
|
294
|
+
const month = dt.getMonth()
|
|
295
|
+
const day = dt.getDay()
|
|
296
|
+
const hour = dt.getHour()
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
#### Translations & Meta
|
|
300
|
+
|
|
301
|
+
**Translate** — Translation system with caching
|
|
302
|
+
```typescript
|
|
303
|
+
const text = await Translate.get('welcome.message')
|
|
304
|
+
const withVars = await Translate.get('hello.user', { name: 'John' })
|
|
305
|
+
const texts = await Translate.getList(['save', 'cancel', 'submit'])
|
|
306
|
+
const cached = Translate.getSync('button.save') // sync from cache
|
|
307
|
+
Translate.init(() => import('./translations.json'))
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**Meta** — HTML meta tags management
|
|
311
|
+
```typescript
|
|
312
|
+
const meta = new Meta()
|
|
313
|
+
meta.set('title', 'Page Title')
|
|
314
|
+
meta.set('description', 'Page description')
|
|
315
|
+
meta.set('keywords', 'key1, key2, key3')
|
|
316
|
+
meta.render() // apply to DOM
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**MetaManager** — Manage multiple meta instances
|
|
320
|
+
```typescript
|
|
321
|
+
MetaManager.add('page1', { title: 'Page 1', description: 'First page' })
|
|
322
|
+
MetaManager.add('page2', { title: 'Page 2', description: 'Second page' })
|
|
323
|
+
MetaManager.render() // render all
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**MetaOg** — Open Graph meta tags
|
|
327
|
+
```typescript
|
|
328
|
+
const og = new MetaOg()
|
|
329
|
+
og.set('title', 'Share Title')
|
|
330
|
+
og.set('description', 'Share description')
|
|
331
|
+
og.set('image', 'https://example.com/image.jpg')
|
|
332
|
+
og.render()
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**MetaTwitter** — Twitter Card meta tags
|
|
336
|
+
```typescript
|
|
337
|
+
const twitter = new MetaTwitter()
|
|
338
|
+
twitter.set('title', 'Tweet Title')
|
|
339
|
+
twitter.set('description', 'Tweet description')
|
|
340
|
+
twitter.set('image', 'https://example.com/image.jpg')
|
|
341
|
+
twitter.render()
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
#### Icons & Communication
|
|
345
|
+
|
|
346
|
+
**Icons** — SVG icon management
|
|
347
|
+
```typescript
|
|
348
|
+
Icons.add('user', '/icons/user.svg')
|
|
349
|
+
const icon = await Icons.get('user')
|
|
350
|
+
const autoPath = await Icons.get('@arrow-left') // auto path
|
|
351
|
+
if (Icons.is('settings')) { /* icon exists */ }
|
|
352
|
+
Icons.addPath('/assets/icons/')
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**BroadcastMessage** — Cross-tab communication
|
|
356
|
+
```typescript
|
|
357
|
+
const broadcast = new BroadcastMessage('app-channel')
|
|
358
|
+
broadcast.on('update', (data) => console.log('Received:', data))
|
|
359
|
+
broadcast.send('update', { userId: 123, action: 'refresh' })
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### Events & DOM
|
|
363
|
+
|
|
364
|
+
**EventItem** — DOM event management
|
|
365
|
+
```typescript
|
|
366
|
+
const event = new EventItem(element, 'click', handler)
|
|
367
|
+
event.start() // start listening
|
|
368
|
+
event.stop() // stop listening
|
|
369
|
+
event.toggle(true) // enable/disable
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**ScrollbarWidth** — Detect scrollbar width
|
|
373
|
+
```typescript
|
|
374
|
+
const width = await ScrollbarWidth.get() // width in px
|
|
375
|
+
const shouldHide = await ScrollbarWidth.is() // thin scrollbar?
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### 🔧 Functions
|
|
379
|
+
|
|
380
|
+
#### Array Operations
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
toArray(value) // convert to array
|
|
384
|
+
arrFill(value, count) // create array with repetitions
|
|
385
|
+
forEach(data, callback) // universal iteration
|
|
386
|
+
uniqueArray(array) // remove duplicates
|
|
387
|
+
inArray(array, value) // check presence
|
|
388
|
+
getColumn(array, column) // extract column
|
|
389
|
+
splice(array, start, count) // immutable splice
|
|
390
|
+
getLengthOfAllArray(...arrays) // total length of arrays
|
|
391
|
+
getMaxLengthAllArray(...arrays) // max length
|
|
392
|
+
getMinLengthAllArray(...arrays) // min length
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
#### Object Operations
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
copyObject(obj) // deep copy
|
|
399
|
+
isObjectNotArray(value) // check object (not array)
|
|
400
|
+
getObjectByKeys(obj, keys) // extract by keys
|
|
401
|
+
getObjectNoUndefined(obj) // remove undefined
|
|
402
|
+
getObjectOrNone(obj) // get object or empty
|
|
403
|
+
replaceRecursive(data, search, replace) // recursive replace
|
|
404
|
+
setValues(obj, keys, value) // set values
|
|
405
|
+
intersectKey(data1, data2) // key intersection
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
#### String Operations
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
toCamelCase(str) // to camelCase
|
|
412
|
+
toKebabCase(str) // to kebab-case
|
|
413
|
+
toCamelCaseFirst(str) // capitalize first
|
|
414
|
+
anyToString(value) // universal string conversion
|
|
415
|
+
strFill(value, length, fill) // fill string
|
|
416
|
+
replaceTemplate(str, replacements) // replace templates
|
|
417
|
+
applyTemplate(text, data) // apply template
|
|
418
|
+
getRandomText(min, max) // generate random text
|
|
419
|
+
encodeAttribute(value) // encode for HTML attribute
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
#### DOM Operations
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
getElement(selector) // get element
|
|
426
|
+
createElement(parent, tag, options) // create element
|
|
427
|
+
getElementId(element) // get/generate ID
|
|
428
|
+
getAttributes(element) // get attributes
|
|
429
|
+
setElementItem(element, name, value) // set attribute
|
|
430
|
+
getElementItem(element, name) // get attribute
|
|
431
|
+
getElementOrWindow(element) // get element or window
|
|
432
|
+
domQuerySelector(selector) // safe querySelector
|
|
433
|
+
domQuerySelectorAll(selector) // safe querySelectorAll
|
|
434
|
+
goScroll(element, options) // smooth scroll
|
|
435
|
+
initScrollbarOffset() // scrollbar compensation
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
#### Events
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
eventStopPropagation(event) // stop propagation
|
|
442
|
+
getKey(event) // get pressed key
|
|
443
|
+
getMouseClient(event) // mouse/touch coordinates
|
|
444
|
+
getMouseClientX(event) // X coordinate
|
|
445
|
+
getMouseClientY(event) // Y coordinate
|
|
446
|
+
getClipboardData(event) // clipboard data
|
|
447
|
+
writeClipboardData(text) // write to clipboard
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
#### Validation
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
isFilled(value) // check if filled
|
|
454
|
+
isArray(value) // check array
|
|
455
|
+
isObject(value) // check object
|
|
456
|
+
isString(value) // check string
|
|
457
|
+
isNumber(value) // check number
|
|
458
|
+
isFunction(value) // check function
|
|
459
|
+
isFloat(value) // check float
|
|
460
|
+
isIntegerBetween(value, min, max) // check range
|
|
461
|
+
isDifferent(value1, value2) // check difference
|
|
462
|
+
isNull(value) // check null/undefined
|
|
463
|
+
isDomRuntime() // check browser environment
|
|
464
|
+
isWindow(element) // check window
|
|
465
|
+
isInDom(element) // check if in DOM
|
|
466
|
+
isSelected(value, check) // check selection
|
|
467
|
+
isSelectedByList(list, value) // check in list
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
#### Math Operations
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
random(min, max) // random number
|
|
474
|
+
getStepPercent(value, min, max) // percent from range
|
|
475
|
+
getStepValue(percent, min, max) // value by percent
|
|
476
|
+
toNumber(value) // convert to number
|
|
477
|
+
toNumberByMax(value, max) // with limit
|
|
478
|
+
toPercent(value) // to percent
|
|
479
|
+
toPercentBy100(value) // to percent (divide by 100)
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
#### Conversions
|
|
483
|
+
|
|
484
|
+
```typescript
|
|
485
|
+
transformation(value) // universal conversion
|
|
486
|
+
toDate(value) // to Date object
|
|
487
|
+
secondToTime(seconds) // seconds to time format
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
#### Execution Utilities
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
executeFunction(callback) // execute function or return value
|
|
494
|
+
executePromise(callback) // async execution
|
|
495
|
+
frame(callback, next, end) // requestAnimationFrame loop
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
#### Data Operations
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
getItemByPath(obj, path) // get by path
|
|
502
|
+
getExp(value, flags) // create RegExp with escaping
|
|
503
|
+
getRequestString(data) // object to query string
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### 📝 TypeScript Types
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
// Basic types
|
|
510
|
+
Undefined // undefined | null
|
|
511
|
+
EmptyValue // all "empty" values
|
|
512
|
+
NumberOrString // number | string
|
|
513
|
+
NumberOrStringOrBoolean // number | string | boolean
|
|
514
|
+
NumberOrStringOrDate // number | string | Date
|
|
515
|
+
NormalOrArray<T> // T | T[]
|
|
516
|
+
FunctionArgs<Args, Return> // typed function
|
|
517
|
+
FunctionReturn<R> // () => R
|
|
518
|
+
ObjectOrArray<T> // Record | T[]
|
|
519
|
+
ItemList // Record<string, any>
|
|
520
|
+
ElementOrString<E> // E | string | Window
|
|
521
|
+
|
|
522
|
+
// API types
|
|
523
|
+
ApiMethodItem // enum: GET, POST, PUT, DELETE
|
|
524
|
+
ApiMethod // HTTP method type
|
|
525
|
+
ApiFetch // API request options
|
|
526
|
+
ApiPreparationEnd // preparation callback result
|
|
527
|
+
ApiDefaultValue // default request data
|
|
528
|
+
ApiStatusItem // status information
|
|
529
|
+
ApiResponseItem // response cache item
|
|
530
|
+
|
|
531
|
+
// Geo types
|
|
532
|
+
GeoItemFull // full country information
|
|
533
|
+
GeoFlagItem // flag information
|
|
534
|
+
GeoPhoneValue // phone data
|
|
535
|
+
GeoDate // date format types
|
|
536
|
+
|
|
537
|
+
// Meta types
|
|
538
|
+
MetaItemType // meta tag types
|
|
539
|
+
MetaManagerItemType // meta manager item
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
## 🎯 Usage Examples
|
|
543
|
+
|
|
544
|
+
### Caching Expensive Operations
|
|
545
|
+
|
|
546
|
+
```typescript
|
|
547
|
+
import { Cache } from '@dxtmisha/functional-basic'
|
|
548
|
+
|
|
549
|
+
const cache = new Cache()
|
|
550
|
+
let userId = 1
|
|
551
|
+
|
|
552
|
+
// Data is cached on first call
|
|
553
|
+
const userData = cache.get(
|
|
554
|
+
'user-data',
|
|
555
|
+
() => {
|
|
556
|
+
console.log('Loading user data...')
|
|
557
|
+
return fetchUserData(userId)
|
|
558
|
+
},
|
|
559
|
+
[userId] // dependencies for invalidation
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
// When userId changes, cache invalidates
|
|
563
|
+
userId = 2
|
|
564
|
+
const newUserData = cache.get('user-data', () => fetchUserData(userId), [userId])
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Locale-Aware Formatting
|
|
568
|
+
|
|
569
|
+
```typescript
|
|
570
|
+
import { GeoIntl } from '@dxtmisha/functional-basic'
|
|
571
|
+
|
|
572
|
+
const intl = new GeoIntl('en-US')
|
|
573
|
+
|
|
574
|
+
// Numbers
|
|
575
|
+
console.log(intl.number(1234567.89)) // '1,234,567.89'
|
|
576
|
+
|
|
577
|
+
// Currency
|
|
578
|
+
console.log(intl.currency(99.99, 'USD')) // '$99.99'
|
|
579
|
+
|
|
580
|
+
// Dates
|
|
581
|
+
console.log(intl.date(new Date(), 'long')) // 'October 15, 2024'
|
|
582
|
+
|
|
583
|
+
// Relative time
|
|
584
|
+
console.log(intl.relative(-2, 'day')) // '2 days ago'
|
|
585
|
+
|
|
586
|
+
// Plural forms
|
|
587
|
+
console.log(intl.plural(5, ['item', 'items'])) // '5 items'
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### REST API Client
|
|
591
|
+
|
|
592
|
+
```typescript
|
|
593
|
+
import { Api } from '@dxtmisha/functional-basic'
|
|
594
|
+
|
|
595
|
+
// Setup
|
|
596
|
+
Api.setUrl('/api/v1')
|
|
597
|
+
Api.setHeaders({
|
|
598
|
+
Authorization: 'Bearer token',
|
|
599
|
+
'Content-Type': 'application/json'
|
|
600
|
+
})
|
|
601
|
+
|
|
602
|
+
// GET request
|
|
603
|
+
const users = await Api.get({ path: 'users' })
|
|
604
|
+
|
|
605
|
+
// POST with data
|
|
606
|
+
const newUser = await Api.post({
|
|
607
|
+
path: 'users',
|
|
608
|
+
request: { name: 'John Doe', email: 'john@example.com' }
|
|
609
|
+
})
|
|
610
|
+
|
|
611
|
+
// PUT
|
|
612
|
+
await Api.put({
|
|
613
|
+
path: 'users/123',
|
|
614
|
+
request: { name: 'Jane Doe' }
|
|
615
|
+
})
|
|
616
|
+
|
|
617
|
+
// DELETE
|
|
618
|
+
await Api.delete({ path: 'users/123' })
|
|
619
|
+
|
|
620
|
+
// With query parameters
|
|
621
|
+
const filtered = await Api.get({
|
|
622
|
+
path: 'users',
|
|
623
|
+
request: { role: 'admin', active: true }
|
|
624
|
+
})
|
|
625
|
+
// GET /api/v1/users?role=admin&active=true
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### URL Hash Management
|
|
629
|
+
|
|
630
|
+
```typescript
|
|
631
|
+
import { Hash } from '@dxtmisha/functional-basic'
|
|
632
|
+
|
|
633
|
+
// Set parameters
|
|
634
|
+
Hash.set('page', 'products')
|
|
635
|
+
Hash.set('category', 'electronics')
|
|
636
|
+
Hash.set('sort', 'price')
|
|
637
|
+
|
|
638
|
+
// URL becomes: #page:products/category:electronics/sort:price
|
|
639
|
+
|
|
640
|
+
// Get parameters
|
|
641
|
+
const page = Hash.get('page', 'home')
|
|
642
|
+
const category = Hash.get('category')
|
|
643
|
+
|
|
644
|
+
// Watch for changes
|
|
645
|
+
Hash.addWatch('page', (newPage) => {
|
|
646
|
+
console.log(`Page changed to: ${newPage}`)
|
|
647
|
+
loadPageContent(newPage)
|
|
648
|
+
})
|
|
649
|
+
|
|
650
|
+
// Bulk update
|
|
651
|
+
Hash.go({ page: 'checkout', step: '1' })
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### Meta Tags Management
|
|
655
|
+
|
|
656
|
+
```typescript
|
|
657
|
+
import { Meta, MetaOg, MetaTwitter } from '@dxtmisha/functional-basic'
|
|
658
|
+
|
|
659
|
+
// Basic meta tags
|
|
660
|
+
const meta = new Meta()
|
|
661
|
+
meta.set('title', 'My Awesome Page')
|
|
662
|
+
meta.set('description', 'This is an awesome page description')
|
|
663
|
+
meta.set('keywords', 'awesome, page, example')
|
|
664
|
+
meta.render()
|
|
665
|
+
|
|
666
|
+
// Open Graph
|
|
667
|
+
const og = new MetaOg()
|
|
668
|
+
og.set('title', 'Share Title')
|
|
669
|
+
og.set('description', 'Share description for social media')
|
|
670
|
+
og.set('image', 'https://example.com/share-image.jpg')
|
|
671
|
+
og.set('url', 'https://example.com/page')
|
|
672
|
+
og.render()
|
|
673
|
+
|
|
674
|
+
// Twitter Card
|
|
675
|
+
const twitter = new MetaTwitter()
|
|
676
|
+
twitter.set('title', 'Tweet Title')
|
|
677
|
+
twitter.set('description', 'Tweet description')
|
|
678
|
+
twitter.set('image', 'https://example.com/twitter-image.jpg')
|
|
679
|
+
twitter.render()
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
### Cross-Tab Communication
|
|
683
|
+
|
|
684
|
+
```typescript
|
|
685
|
+
import { BroadcastMessage } from '@dxtmisha/functional-basic'
|
|
686
|
+
|
|
687
|
+
const channel = new BroadcastMessage('app-sync')
|
|
688
|
+
|
|
689
|
+
// Listen for messages
|
|
690
|
+
channel.on('user-updated', (data) => {
|
|
691
|
+
console.log('User updated in another tab:', data)
|
|
692
|
+
updateUI(data)
|
|
693
|
+
})
|
|
694
|
+
|
|
695
|
+
// Send message to all tabs
|
|
696
|
+
channel.send('user-updated', {
|
|
697
|
+
userId: 123,
|
|
698
|
+
name: 'John Doe',
|
|
699
|
+
timestamp: Date.now()
|
|
700
|
+
})
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### Date Operations
|
|
704
|
+
|
|
705
|
+
```typescript
|
|
706
|
+
import { Datetime } from '@dxtmisha/functional-basic'
|
|
707
|
+
|
|
708
|
+
const dt = new Datetime('2024-10-15 14:30:00', 'full', 'en-US')
|
|
709
|
+
|
|
710
|
+
// Formatting
|
|
711
|
+
console.log(dt.format('YYYY-MM-DD')) // '2024-10-15'
|
|
712
|
+
console.log(dt.format('DD/MM/YYYY HH:mm')) // '15/10/2024 14:30'
|
|
713
|
+
console.log(dt.toIso()) // '2024-10-15T14:30:00.000Z'
|
|
714
|
+
|
|
715
|
+
// Manipulation
|
|
716
|
+
dt.moveByDay(7) // +7 days
|
|
717
|
+
dt.moveByMonth(-1) // -1 month
|
|
718
|
+
dt.moveByHour(3) // +3 hours
|
|
719
|
+
|
|
720
|
+
// Getters
|
|
721
|
+
console.log(dt.getYear()) // 2024
|
|
722
|
+
console.log(dt.getMonth()) // 10
|
|
723
|
+
console.log(dt.getDay()) // 15
|
|
724
|
+
console.log(dt.getDayWeek()) // 2 (Tuesday)
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
## 📁 Export Structure
|
|
728
|
+
|
|
729
|
+
```
|
|
730
|
+
@dxtmisha/functional-basic
|
|
731
|
+
├── / # All utilities (recommended)
|
|
732
|
+
└── /types/* # TypeScript types
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
## 🔧 Requirements
|
|
736
|
+
|
|
737
|
+
- **Node.js**: ≥18.0.0
|
|
738
|
+
- **TypeScript**: 4.0+ (optional)
|
|
739
|
+
- **No Framework Required**: Works with vanilla JS or any framework
|
|
740
|
+
|
|
741
|
+
## 🤝 Compatibility
|
|
742
|
+
|
|
743
|
+
| Environment | Support |
|
|
744
|
+
|-------------|---------|
|
|
745
|
+
| **Browsers** | ✅ ES2020+ (Chrome 80+, Firefox 72+, Safari 13.1+, Edge 80+) |
|
|
746
|
+
| **Node.js** | ✅ 18+ |
|
|
747
|
+
| **TypeScript** | ✅ 4+ |
|
|
748
|
+
| **Vanilla JS** | ✅ Full support |
|
|
749
|
+
| **React** | ✅ Full support |
|
|
750
|
+
| **Vue** | ✅ Full support |
|
|
751
|
+
| **Angular** | ✅ Full support |
|
|
752
|
+
| **SSR** | ✅ With environment checks |
|
|
753
|
+
|
|
754
|
+
## 📊 Bundle Size
|
|
755
|
+
|
|
756
|
+
- **Full library**: ~90KB (minified)
|
|
757
|
+
- **With tree-shaking**: only used functions are imported
|
|
758
|
+
- **Minimal import**: ~1KB (single function)
|
|
759
|
+
- **Dependencies**: zero runtime dependencies
|
|
760
|
+
|
|
761
|
+
## 🎯 Tree-shaking
|
|
762
|
+
|
|
763
|
+
The library fully supports tree-shaking:
|
|
764
|
+
|
|
765
|
+
```typescript
|
|
766
|
+
// ❌ Bad - imports entire library
|
|
767
|
+
import * as functional from '@dxtmisha/functional-basic'
|
|
768
|
+
|
|
769
|
+
// ✅ Good - imports only what's needed
|
|
770
|
+
import { toArray, forEach, Cache } from '@dxtmisha/functional-basic'
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
## 🏆 Advantages
|
|
774
|
+
|
|
775
|
+
- ✅ **Full TypeScript support** — TypeScript out of the box
|
|
776
|
+
- ✅ **Zero dependencies** — no external runtime dependencies
|
|
777
|
+
- ✅ **Framework agnostic** — works with any framework or vanilla JS
|
|
778
|
+
- ✅ **Tree-shakeable** — optimal bundle size
|
|
779
|
+
- ✅ **SSR friendly** — safe for server-side rendering
|
|
780
|
+
- ✅ **Comprehensive** — everything you need in one library
|
|
781
|
+
- ✅ **Well tested** — comprehensive test coverage
|
|
782
|
+
- ✅ **Well documented** — complete API documentation
|
|
783
|
+
- ✅ **Production ready** — used in production projects
|
|
784
|
+
- ✅ **Regular updates** — active development and support
|
|
785
|
+
|
|
786
|
+
## 📖 Difference from @dxtmisha/functional
|
|
787
|
+
|
|
788
|
+
- **@dxtmisha/functional-basic**: Core utilities without Vue dependencies
|
|
789
|
+
- **@dxtmisha/functional**: Includes Vue composables and reactive utilities
|
|
790
|
+
|
|
791
|
+
Use `functional-basic` if:
|
|
792
|
+
- You're not using Vue.js
|
|
793
|
+
- You want minimal dependencies
|
|
794
|
+
- You're building a library
|
|
795
|
+
- You need maximum compatibility
|
|
796
|
+
|
|
797
|
+
Use `functional` if:
|
|
798
|
+
- You're building a Vue.js application
|
|
799
|
+
- You want reactive composables
|
|
800
|
+
- You need Vue-specific utilities
|
|
801
|
+
|
|
802
|
+
## 📖 Additional Documentation
|
|
803
|
+
|
|
804
|
+
- [GitHub Repository](https://github.com/dxtmisha/dxt-ui)
|
|
805
|
+
- [Full API Documentation](https://github.com/dxtmisha/dxt-ui/tree/main/packages/wiki/src/media/functional-basic)
|
|
806
|
+
- [DXT UI Documentation](https://dxtmisha.github.io/dxt-ui/)
|
|
807
|
+
|
|
808
|
+
## 🐛 Report Issues
|
|
809
|
+
|
|
810
|
+
If you found a bug or want to suggest an improvement:
|
|
811
|
+
|
|
812
|
+
- [GitHub Issues](https://github.com/dxtmisha/dxt-ui/issues)
|
|
813
|
+
|
|
814
|
+
## 📄 License
|
|
815
|
+
|
|
816
|
+
MIT © [dxtmisha](https://github.com/dxtmisha)
|
|
817
|
+
|
|
818
|
+
## 🤝 Contributing
|
|
819
|
+
|
|
820
|
+
Pull requests are welcome! For major changes, please open an issue first to discuss what you would like to change.
|
|
821
|
+
|
|
822
|
+
## ⭐ Support the Project
|
|
823
|
+
|
|
824
|
+
If this library was helpful, please star it on [GitHub](https://github.com/dxtmisha/dxt-ui)!
|
|
825
|
+
|
|
826
|
+
---
|
|
827
|
+
|
|
828
|
+
Made with ❤️ by [dxtmisha](https://github.com/dxtmisha)
|
|
829
|
+
|