@pivanov/utils 0.0.2 โ 0.0.3
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/README.md +257 -217
- package/dist/cjs/index.js +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/index.d.ts +3 -129
- package/package.json +16 -18
package/README.md
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
1
|
# @pivanov/utils
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
<i>A comprehensive collection of TypeScript utilities for modern web development</i>
|
|
5
|
+
<br />
|
|
6
|
+
<br />
|
|
7
|
+
<img src="https://img.shields.io/npm/v/@pivanov/utils?logo=npm" alt="NPM Version" />
|
|
8
|
+
|
|
9
|
+
<img src="https://img.shields.io/bundlephobia/minzip/@pivanov/utils" alt="Bundle Size" />
|
|
10
|
+
|
|
11
|
+
<img src="https://github.com/pivanov/pivanov-utils/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI Status" />
|
|
12
|
+
|
|
13
|
+
<img src="https://codecov.io/github/pivanov/pivanov-utils/graph/badge.svg?token=EPRKTP7D79" alt="Coverage Status" />
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
## Features
|
|
17
17
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
- ๐ **Well Documented** - Detailed documentation and examples
|
|
25
|
-
- โ
**Well Tested** - Comprehensive test coverage
|
|
18
|
+
- ๐ **Type-Safe** - Full TypeScript support with strict type checking and excellent type inference
|
|
19
|
+
- ๐ฏ **Tree-Shakeable** - Import only what you need for minimal bundle size
|
|
20
|
+
- โก **Performance Focused** - Optimized implementations with no external dependencies
|
|
21
|
+
- ๐งช **Well Tested** - Comprehensive test coverage for reliability
|
|
22
|
+
- ๐ฆ **Modular** - Organized into focused modules for easy navigation
|
|
23
|
+
- ๐ **Well Documented** - Detailed JSDoc comments and examples
|
|
26
24
|
|
|
27
25
|
## Installation
|
|
28
26
|
|
|
29
27
|
```bash
|
|
30
|
-
pnpm
|
|
31
|
-
# or
|
|
32
|
-
yarn add @pivanov/utils
|
|
28
|
+
pnpm add @pivanov/utils
|
|
33
29
|
# or
|
|
34
30
|
npm install @pivanov/utils
|
|
31
|
+
# or
|
|
32
|
+
yarn add @pivanov/utils
|
|
35
33
|
```
|
|
36
34
|
|
|
37
|
-
##
|
|
35
|
+
## Quick Start
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
```typescript
|
|
38
|
+
import { camelCase } from '@pivanov/utils/string';
|
|
39
|
+
import { pick } from '@pivanov/utils/object';
|
|
40
|
+
import { isString } from '@pivanov/utils/assertion';
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
camelCase('foo-bar'); // 'fooBar'
|
|
43
|
+
pick({ name: 'John', age: 30 }, ['name']); // { name: 'John' }
|
|
44
|
+
isString('hello'); // true
|
|
45
|
+
```
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
- **object** - Object manipulation utilities
|
|
45
|
-
- **promise** - Async utilities and promise helpers
|
|
46
|
-
- **string** - String transformation utilities
|
|
47
|
-
- **tools** - Various utilities including DOM, Event Bus, and more
|
|
47
|
+
## API Reference
|
|
48
48
|
|
|
49
|
-
###
|
|
49
|
+
### Type Guards & Assertions
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
Runtime type checking with TypeScript type narrowing.
|
|
52
52
|
|
|
53
53
|
```typescript
|
|
54
54
|
import {
|
|
@@ -58,32 +58,32 @@ import {
|
|
|
58
58
|
isObject,
|
|
59
59
|
isFunction,
|
|
60
60
|
isNull,
|
|
61
|
-
isUndefined
|
|
61
|
+
isUndefined,
|
|
62
62
|
} from '@pivanov/utils/assertion';
|
|
63
63
|
|
|
64
|
-
// Type
|
|
65
|
-
isString('hello'); // type narrowed to string
|
|
66
|
-
isNumber(123); // type narrowed to number
|
|
67
|
-
isBoolean(true); // type narrowed to boolean
|
|
68
|
-
isObject({}); // type narrowed to object
|
|
69
|
-
isFunction(() => {}); // type narrowed to function
|
|
70
|
-
isNull(null); // type narrowed to null
|
|
71
|
-
isUndefined(void 0); // type narrowed to undefined
|
|
72
|
-
|
|
73
|
-
// Use in conditional checks
|
|
64
|
+
// Type guards automatically narrow TypeScript types
|
|
74
65
|
function processValue(value: unknown) {
|
|
75
66
|
if (isString(value)) {
|
|
76
|
-
return value.toUpperCase(); // TypeScript knows
|
|
67
|
+
return value.toUpperCase(); // TypeScript knows this is a string
|
|
77
68
|
}
|
|
78
69
|
if (isNumber(value)) {
|
|
79
|
-
return value.toFixed(2);
|
|
70
|
+
return value.toFixed(2); // TypeScript knows this is a number
|
|
80
71
|
}
|
|
81
72
|
}
|
|
73
|
+
|
|
74
|
+
// Examples
|
|
75
|
+
isString('hello'); // true
|
|
76
|
+
isNumber(42); // true
|
|
77
|
+
isBoolean(true); // true
|
|
78
|
+
isObject({}); // true (plain objects only, not arrays)
|
|
79
|
+
isFunction(() => {}); // true
|
|
80
|
+
isNull(null); // true
|
|
81
|
+
isUndefined(undefined); // true
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
### Object
|
|
84
|
+
### Object Utilities
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
Immutable object manipulation with full type safety.
|
|
87
87
|
|
|
88
88
|
```typescript
|
|
89
89
|
import { pick, omit, merge, deepMerge } from '@pivanov/utils/object';
|
|
@@ -98,8 +98,8 @@ omit(user, ['email']);
|
|
|
98
98
|
// { name: 'John', age: 30 }
|
|
99
99
|
|
|
100
100
|
// Shallow merge
|
|
101
|
-
merge({ a: 1 }, { b:
|
|
102
|
-
// { a: 1, b:
|
|
101
|
+
merge({ a: 1, b: 2 }, { b: 3, c: 4 });
|
|
102
|
+
// { a: 1, b: 3, c: 4 }
|
|
103
103
|
|
|
104
104
|
// Deep merge with nested structures
|
|
105
105
|
deepMerge(
|
|
@@ -109,24 +109,9 @@ deepMerge(
|
|
|
109
109
|
// { user: { name: 'John', settings: { theme: 'dark', fontSize: 14 } } }
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
###
|
|
112
|
+
### String Utilities
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
import { sleep } from '@pivanov/utils/promise';
|
|
118
|
-
|
|
119
|
-
// Pause execution for specified duration
|
|
120
|
-
async function example() {
|
|
121
|
-
console.log('Start');
|
|
122
|
-
await sleep(1000); // Wait for 1 second
|
|
123
|
-
console.log('End');
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### String Module
|
|
128
|
-
|
|
129
|
-
String manipulation utilities with TypeScript support.
|
|
114
|
+
String transformation utilities with TypeScript template literal types support.
|
|
130
115
|
|
|
131
116
|
```typescript
|
|
132
117
|
import {
|
|
@@ -136,234 +121,289 @@ import {
|
|
|
136
121
|
slugify,
|
|
137
122
|
capitalize,
|
|
138
123
|
uncapitalize,
|
|
139
|
-
capitalizeFirstLetter
|
|
124
|
+
capitalizeFirstLetter,
|
|
140
125
|
} from '@pivanov/utils/string';
|
|
141
126
|
|
|
142
127
|
// Case transformations
|
|
143
|
-
camelCase('foo-bar');
|
|
144
|
-
|
|
145
|
-
kebabCase('fooBar'); // 'foo-bar'
|
|
146
|
-
slugify('Hello World!'); // 'hello-world'
|
|
128
|
+
camelCase('foo-bar'); // 'fooBar'
|
|
129
|
+
camelCase('FOO_BAR'); // 'fooBar'
|
|
147
130
|
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
uncapitalize('Hello'); // 'hello' (with type Uncapitalize<'Hello'>)
|
|
151
|
-
capitalizeFirstLetter('hello world'); // 'Hello world'
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Tools Module
|
|
131
|
+
pascalCase('foo-bar'); // 'FooBar'
|
|
132
|
+
pascalCase('foo_bar_baz'); // 'FooBarBaz'
|
|
155
133
|
|
|
156
|
-
|
|
134
|
+
kebabCase('fooBar'); // 'foo-bar'
|
|
135
|
+
kebabCase('XMLHttpRequest'); // 'xml-http-request'
|
|
157
136
|
|
|
158
|
-
|
|
137
|
+
slugify('Hello World!'); // 'hello-world'
|
|
138
|
+
slugify('รber Cafรฉ'); // 'uber-cafe'
|
|
159
139
|
|
|
160
|
-
|
|
161
|
-
|
|
140
|
+
// Capitalization with TypeScript support
|
|
141
|
+
capitalize('hello'); // 'Hello' (type: Capitalize<'hello'>)
|
|
142
|
+
uncapitalize('Hello'); // 'hello' (type: Uncapitalize<'Hello'>)
|
|
143
|
+
capitalizeFirstLetter('hello world'); // 'Hello world'
|
|
144
|
+
```
|
|
162
145
|
|
|
163
|
-
|
|
164
|
-
const isVisible = checkVisibility(element);
|
|
146
|
+
**When to use each:**
|
|
165
147
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
});
|
|
171
|
-
```
|
|
148
|
+
- `camelCase`: JavaScript/TypeScript variables and properties
|
|
149
|
+
- `pascalCase`: Class names, React components, TypeScript types
|
|
150
|
+
- `kebabCase`: CSS classes, HTML attributes, file names
|
|
151
|
+
- `slugify`: URL slugs (more aggressive than kebabCase)
|
|
172
152
|
|
|
173
|
-
|
|
153
|
+
### Promise Utilities
|
|
174
154
|
|
|
175
|
-
|
|
155
|
+
Simple async utilities for better promise handling.
|
|
176
156
|
|
|
177
157
|
```typescript
|
|
178
|
-
import {
|
|
179
|
-
|
|
180
|
-
// Define your events interface
|
|
181
|
-
interface Events {
|
|
182
|
-
'user-logged-in': { id: number; name: string };
|
|
183
|
-
'data-updated': { timestamp: number };
|
|
184
|
-
}
|
|
158
|
+
import { sleep } from '@pivanov/utils/promise';
|
|
185
159
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
});
|
|
160
|
+
async function example() {
|
|
161
|
+
console.log('Start');
|
|
162
|
+
await sleep(1000); // Wait 1 second
|
|
163
|
+
console.log('Done');
|
|
191
164
|
}
|
|
192
|
-
|
|
193
|
-
// Direct subscription
|
|
194
|
-
const unsubscribe = busSubscribe('data-updated', (data) => {
|
|
195
|
-
console.log('Data updated at:', data.timestamp);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
// Dispatch events
|
|
199
|
-
busDispatch('user-logged-in', { id: 1, name: 'John' });
|
|
200
165
|
```
|
|
201
166
|
|
|
202
|
-
|
|
167
|
+
### Deep Clone & Equality
|
|
168
|
+
|
|
169
|
+
High-performance deep cloning and comparison with circular reference handling.
|
|
203
170
|
|
|
204
171
|
```typescript
|
|
205
172
|
import { deepClone, isEqual } from '@pivanov/utils/tools';
|
|
206
173
|
|
|
207
|
-
// Deep clone
|
|
174
|
+
// Deep clone complex structures
|
|
208
175
|
const original = {
|
|
209
176
|
nested: { array: [1, 2, { value: 3 }] },
|
|
210
177
|
date: new Date(),
|
|
211
|
-
|
|
178
|
+
map: new Map([['key', 'value']]),
|
|
179
|
+
set: new Set([1, 2, 3]),
|
|
180
|
+
circular: {} as any,
|
|
212
181
|
};
|
|
213
|
-
original.circular = original;
|
|
182
|
+
original.circular = original; // Circular reference
|
|
183
|
+
|
|
214
184
|
const cloned = deepClone(original);
|
|
185
|
+
// Fully independent copy with circular references preserved
|
|
215
186
|
|
|
216
187
|
// Deep equality comparison
|
|
217
188
|
isEqual({ a: [1, 2, 3] }, { a: [1, 2, 3] }); // true
|
|
218
189
|
isEqual(new Date('2024-01-01'), new Date('2024-01-01')); // true
|
|
219
190
|
isEqual([1, 2, [3, 4]], [1, 2, [3, 4]]); // true
|
|
191
|
+
isEqual(new Set([1, 2]), new Set([2, 1])); // false (Sets maintain order)
|
|
220
192
|
```
|
|
221
193
|
|
|
222
|
-
|
|
194
|
+
**Supported types:**
|
|
223
195
|
|
|
224
|
-
|
|
196
|
+
- Primitives (string, number, boolean, null, undefined, symbol, bigint)
|
|
197
|
+
- Arrays (including sparse arrays)
|
|
198
|
+
- Plain objects (with getters/setters)
|
|
199
|
+
- Date, RegExp, Map, Set
|
|
200
|
+
- TypedArrays, ArrayBuffer, Buffer
|
|
201
|
+
- Circular references
|
|
202
|
+
|
|
203
|
+
### Event Bus
|
|
204
|
+
|
|
205
|
+
Type-safe, lightweight event bus with React hooks support.
|
|
225
206
|
|
|
226
207
|
```typescript
|
|
227
|
-
import {
|
|
208
|
+
import { busDispatch, busSubscribe, useEventBus } from '@pivanov/utils/tools';
|
|
228
209
|
|
|
229
|
-
//
|
|
230
|
-
|
|
210
|
+
// Define your application events (optional but recommended)
|
|
211
|
+
type AppEvents = {
|
|
212
|
+
'user-logged-in': { id: number; name: string };
|
|
213
|
+
'data-updated': { timestamp: number };
|
|
214
|
+
'notification': { message: string; type: 'info' | 'error' };
|
|
215
|
+
};
|
|
231
216
|
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
});
|
|
217
|
+
// React hook usage (auto-unsubscribes on unmount)
|
|
218
|
+
function UserProfile() {
|
|
219
|
+
useEventBus<AppEvents>('user-logged-in', (data) => {
|
|
220
|
+
console.log(`Welcome ${data.name}!`);
|
|
221
|
+
});
|
|
236
222
|
|
|
237
|
-
|
|
238
|
-
|
|
223
|
+
return <div>Profile</div>;
|
|
224
|
+
}
|
|
239
225
|
|
|
240
|
-
//
|
|
241
|
-
const
|
|
226
|
+
// Vanilla JavaScript usage
|
|
227
|
+
const unsubscribe = busSubscribe<AppEvents>('data-updated', (data) => {
|
|
228
|
+
console.log('Updated at:', data.timestamp);
|
|
229
|
+
});
|
|
242
230
|
|
|
243
|
-
//
|
|
244
|
-
|
|
231
|
+
// Later: cleanup
|
|
232
|
+
unsubscribe();
|
|
245
233
|
|
|
246
|
-
//
|
|
247
|
-
|
|
234
|
+
// Dispatch events anywhere in your app
|
|
235
|
+
busDispatch<AppEvents>('user-logged-in', { id: 1, name: 'John' });
|
|
236
|
+
busDispatch<AppEvents>('notification', {
|
|
237
|
+
message: 'Settings saved',
|
|
238
|
+
type: 'info',
|
|
239
|
+
});
|
|
248
240
|
```
|
|
249
241
|
|
|
250
|
-
|
|
242
|
+
**Features:**
|
|
243
|
+
|
|
244
|
+
- Fully type-safe with TypeScript
|
|
245
|
+
- Works across React and vanilla JavaScript
|
|
246
|
+
- Automatic cleanup with React hook
|
|
247
|
+
- Uses hashed topic names to avoid collisions
|
|
248
|
+
- Zero dependencies
|
|
251
249
|
|
|
252
|
-
|
|
250
|
+
### Browser Cache API
|
|
253
251
|
|
|
254
|
-
|
|
252
|
+
Type-safe wrapper around the browser Cache API with expiration support.
|
|
255
253
|
|
|
256
|
-
Create a single file for your widget (e.g., `sparkline-widget.ts`):
|
|
257
254
|
```typescript
|
|
258
|
-
import {
|
|
255
|
+
import {
|
|
256
|
+
storageSetItem,
|
|
257
|
+
storageGetItem,
|
|
258
|
+
storageRemoveItem,
|
|
259
|
+
storageExists,
|
|
260
|
+
storageClear,
|
|
261
|
+
storageClearByPrefixOrSuffix,
|
|
262
|
+
storageGetAllKeys,
|
|
263
|
+
storageCalculateSize,
|
|
264
|
+
} from '@pivanov/utils/tools';
|
|
265
|
+
|
|
266
|
+
const CACHE_NAME = 'my-app-cache';
|
|
267
|
+
|
|
268
|
+
// Store data
|
|
269
|
+
await storageSetItem(CACHE_NAME, 'user-data', {
|
|
270
|
+
id: 1,
|
|
271
|
+
name: 'John',
|
|
272
|
+
bigNumber: BigInt(9007199254740991), // BigInt support!
|
|
273
|
+
});
|
|
259
274
|
|
|
260
|
-
//
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
</div>
|
|
275
|
+
// Retrieve data
|
|
276
|
+
const userData = await storageGetItem<{ id: number; name: string }>(
|
|
277
|
+
CACHE_NAME,
|
|
278
|
+
'user-data'
|
|
265
279
|
);
|
|
266
280
|
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
281
|
+
// Check existence
|
|
282
|
+
const exists = await storageExists(CACHE_NAME, 'user-data');
|
|
283
|
+
|
|
284
|
+
// Remove specific item
|
|
285
|
+
await storageRemoveItem(CACHE_NAME, 'user-data');
|
|
286
|
+
|
|
287
|
+
// Get all keys
|
|
288
|
+
const keys = await storageGetAllKeys(CACHE_NAME);
|
|
289
|
+
|
|
290
|
+
// Clear items by prefix
|
|
291
|
+
await storageClearByPrefixOrSuffix(CACHE_NAME, 'temp-', true);
|
|
292
|
+
|
|
293
|
+
// Clear items by suffix
|
|
294
|
+
await storageClearByPrefixOrSuffix(CACHE_NAME, '-cache', false);
|
|
295
|
+
|
|
296
|
+
// Calculate cache size (in bytes)
|
|
297
|
+
const totalSize = await storageCalculateSize(CACHE_NAME);
|
|
298
|
+
const itemSize = await storageCalculateSize(CACHE_NAME, 'user-data');
|
|
299
|
+
|
|
300
|
+
// Clear all cache
|
|
301
|
+
await storageClear(CACHE_NAME);
|
|
276
302
|
```
|
|
277
303
|
|
|
278
|
-
|
|
304
|
+
**Features:**
|
|
305
|
+
|
|
306
|
+
- Automatic JSON serialization/deserialization
|
|
307
|
+
- BigInt support (automatically converted to strings)
|
|
308
|
+
- Type-safe with generics
|
|
309
|
+
- Works with absolute URLs as keys
|
|
310
|
+
- Size calculation utilities
|
|
311
|
+
|
|
312
|
+
### DOM Utilities
|
|
313
|
+
|
|
314
|
+
Browser-safe DOM manipulation helpers.
|
|
279
315
|
|
|
280
316
|
```typescript
|
|
281
|
-
import {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
createRoot(container).render(<App />);
|
|
317
|
+
import {
|
|
318
|
+
isBrowser,
|
|
319
|
+
checkVisibility,
|
|
320
|
+
setStyleProperties,
|
|
321
|
+
calculateRenderedTextWidth,
|
|
322
|
+
} from '@pivanov/utils/tools';
|
|
323
|
+
|
|
324
|
+
// Check if running in browser (SSR-safe)
|
|
325
|
+
if (isBrowser()) {
|
|
326
|
+
// Browser-only code
|
|
327
|
+
window.addEventListener('scroll', handleScroll);
|
|
293
328
|
}
|
|
294
329
|
|
|
295
|
-
|
|
330
|
+
// Check element visibility in viewport
|
|
331
|
+
const element = document.querySelector('.my-element') as HTMLElement;
|
|
332
|
+
if (checkVisibility(element)) {
|
|
333
|
+
element.classList.add('visible');
|
|
334
|
+
}
|
|
296
335
|
|
|
297
|
-
//
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
336
|
+
// Set CSS custom properties
|
|
337
|
+
setStyleProperties(element, {
|
|
338
|
+
'--primary-color': '#3b82f6',
|
|
339
|
+
'--spacing': '1rem',
|
|
340
|
+
'--border-radius': '8px',
|
|
341
|
+
});
|
|
303
342
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
void importWidgets([
|
|
314
|
-
'https://cdn.example.com/widgets/sparkline.js'
|
|
315
|
-
]);
|
|
316
|
-
|
|
317
|
-
renderWidget({
|
|
318
|
-
name: 'sparkline',
|
|
319
|
-
mountTo: document.getElementById('sparkline'),
|
|
320
|
-
widgetProps: {
|
|
321
|
-
color: 'green',
|
|
322
|
-
values: [1, 2, 3, 4, 5]
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
</script>
|
|
326
|
-
</head>
|
|
327
|
-
<body>
|
|
328
|
-
<div id="sparkline"></div>
|
|
329
|
-
</body>
|
|
330
|
-
</html>
|
|
343
|
+
// Calculate text width for dynamic layouts
|
|
344
|
+
const width = calculateRenderedTextWidth('Hello World', 16);
|
|
345
|
+
const widthUppercase = calculateRenderedTextWidth('Hello World', 16, true);
|
|
346
|
+
const widthCustomFont = calculateRenderedTextWidth(
|
|
347
|
+
'Hello World',
|
|
348
|
+
16,
|
|
349
|
+
false,
|
|
350
|
+
'Arial'
|
|
351
|
+
);
|
|
331
352
|
```
|
|
332
353
|
|
|
333
|
-
|
|
334
|
-
- Single-file widget definition
|
|
335
|
-
- Automatic CSS and SVG sprite loading
|
|
336
|
-
- ESM support for browser and bundler usage
|
|
337
|
-
- TypeScript support with proper type inference
|
|
338
|
-
- Works in both React and plain HTML environments
|
|
339
|
-
|
|
340
|
-
### Tree Shaking
|
|
354
|
+
## Tree Shaking
|
|
341
355
|
|
|
342
|
-
Import
|
|
356
|
+
Import only what you need to minimize bundle size:
|
|
343
357
|
|
|
344
358
|
```typescript
|
|
345
|
-
//
|
|
359
|
+
// โ
Good: Import specific utilities
|
|
346
360
|
import { camelCase } from '@pivanov/utils/string';
|
|
347
361
|
import { deepClone } from '@pivanov/utils/tools';
|
|
348
|
-
|
|
362
|
+
|
|
363
|
+
// โ Avoid: Importing everything
|
|
364
|
+
import * as utils from '@pivanov/utils';
|
|
349
365
|
```
|
|
350
366
|
|
|
351
|
-
|
|
367
|
+
## TypeScript Support
|
|
352
368
|
|
|
353
|
-
All utilities
|
|
369
|
+
All utilities provide excellent type inference:
|
|
354
370
|
|
|
355
371
|
```typescript
|
|
356
372
|
import { pick } from '@pivanov/utils/object';
|
|
373
|
+
import { capitalize } from '@pivanov/utils/string';
|
|
374
|
+
|
|
375
|
+
// TypeScript infers exact types
|
|
376
|
+
const user = { name: 'John', age: 30, email: 'john@example.com' } as const;
|
|
377
|
+
const picked = pick(user, ['name', 'email']);
|
|
378
|
+
// Type: { name: "John"; email: "john@example.com" }
|
|
379
|
+
|
|
380
|
+
const str = 'hello' as const;
|
|
381
|
+
const capitalized = capitalize(str);
|
|
382
|
+
// Type: "Hello"
|
|
383
|
+
```
|
|
357
384
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
385
|
+
## Module Overview
|
|
386
|
+
|
|
387
|
+
```text
|
|
388
|
+
@pivanov/utils
|
|
389
|
+
โโโ /assertion - Type guards (isString, isNumber, etc.)
|
|
390
|
+
โโโ /object - Object utilities (pick, omit, merge, deepMerge)
|
|
391
|
+
โโโ /promise - Async utilities (sleep)
|
|
392
|
+
โโโ /string - String utilities (camelCase, kebabCase, etc.)
|
|
393
|
+
โโโ /tools - Various tools
|
|
394
|
+
โโโ deepClone
|
|
395
|
+
โโโ isEqual
|
|
396
|
+
โโโ DOM utilities (isBrowser, checkVisibility, etc.)
|
|
397
|
+
โโโ eventBus (busDispatch, busSubscribe, useEventBus)
|
|
398
|
+
โโโ Cache API (storageSetItem, storageGetItem, etc.)
|
|
361
399
|
```
|
|
362
400
|
|
|
363
|
-
##
|
|
401
|
+
## Browser Compatibility
|
|
364
402
|
|
|
365
|
-
|
|
403
|
+
- Modern browsers with ES2015+ support
|
|
404
|
+
- Cache API requires browser support (Chrome 40+, Firefox 41+, Safari 11.1+)
|
|
405
|
+
- SSR-safe with browser environment detection
|
|
366
406
|
|
|
367
407
|
## License
|
|
368
408
|
|
|
369
|
-
MIT ยฉ Pavel Ivanov
|
|
409
|
+
MIT ยฉ [Pavel Ivanov](https://github.com/pivanov)
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @pivanov/utils v0.0.
|
|
2
|
+
* @pivanov/utils v0.0.3
|
|
3
3
|
* (c) 2024-present Pavel Ivanov
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
* https://github.com/pivanov/utils
|
|
6
6
|
*/
|
|
7
|
-
"use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("react-dom/client");const n=e=>{if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t},o=(e,...t)=>{if(!t.length)return e;const r=t.shift();if(void 0===r)return e;if(n(e)&&n(r))for(const t of Object.keys(r)){const s=r[t];if(n(s)){e[t]||Object.assign(e,{[t]:{}});const r=e[t];n(r)&&o(r,s)}else Object.assign(e,{[t]:s})}return o(e,...t)},s=(e,t)=>"bigint"==typeof t?t.toString():t,i=e=>`${((e,t="๐")=>{let r=0;const n=e+t;for(let e=0;e<n.length;e++)r=(r<<5)-r+n.charCodeAt(e),r|=0;return(r>>>0).toString(16)})(e)}::${e}`,a=(e,t)=>{if(!e)return;const r=i(e);window.dispatchEvent(new CustomEvent(r,{detail:t,bubbles:!0,cancelable:!1}))},c=(e,t)=>{if(!e||"function"!=typeof t)return()=>{};const r=(e=>t=>{if(t instanceof CustomEvent)try{e(t.detail)}catch(e){console.error("Event listener error:",e)}})(t),n=i(e);return window.addEventListener(n,r),()=>window.removeEventListener(n,r)},l=(e,n,o={},s)=>{const i=class extends HTMLElement{constructor(){super();let e=Array.from(document.styleSheets).find((e=>e instanceof CSSStyleSheet&&!e.disabled));if(!e){const t=document.createElement("style");document.head.appendChild(t),e=t.sheet}Array.from(e.cssRules).find((e=>e.selectorText===`${this.tagName.toLowerCase()}`))||e.insertRule(`${this.tagName.toLowerCase()} { display: inline-flex; }`,e.cssRules.length);const t=o.shadow||(s?.length?"open":"closed");if(!o.shadow&&s?.length&&console.info("@@@ Styles are provided but shadowDOM is not enabled"),this.container=t?this.attachShadow({mode:t}):this,s&&t){const e=document.createElement("style");e.textContent=s.join("\n"),this.container.appendChild(e)}}connectedCallback(){this.root||(this.root=r.createRoot(this.container),this.subscribeToEvents())}disconnectedCallback(){this.root&&this.root.unmount()}subscribeToEvents(){c("@@-widget-create",(e=>{const{widgetProps:t,cacheKey:r}=e;this.cacheKey||r!==`${this.tagName.toLowerCase().replace("widget-","")}-${this.uuid}`||(this.cacheKey=r,this.mountComponent(t))})),c("@@-widget-update",(e=>{const{widgetProps:t,cacheKey:r}=e;this.cacheKey===r&&r===`${this.tagName.toLowerCase().replace("widget-","")}-${this.uuid}`&&a("@@-widget-update-props",{widgetProps:t,uuid:this.uuid})}))}mountComponent(e){this.root&&e&&this.root.render(t.jsx(n,{...e,container:this.container,root:this}))}};return customElements.get(e)||customElements.define(e,i),i},f=e.memo((r=>{const{name:n,uuid:o}=r,s=e.useRef(),i=`widget-${n}`;return e.useEffect((()=>{s.current&&(s.current.uuid=o)}),[o]),"string"==typeof o&&o.length?t.jsx(i,{ref:s}):null}),(()=>!0)),u=r=>{const{name:n,uuid:o,widgetProps:s}=r,i=e.useRef(!1),l=o?.trim()||crypto.randomUUID().replace(/-/g,"").slice(0,6),u=n.trim().toLowerCase(),p=`${u}-${l}`,d=!!u.length&&!!l.length;return e.useEffect((()=>{d&&i.current&&a("@@-widget-update",{widgetName:u,widgetProps:s,cacheKey:p})}),[s,l,u,d,p]),c("@@-widgets-loaded",(()=>{i.current=!0,a("@@-widget-create",{widgetName:u,widgetProps:s,cacheKey:p})})),d?t.jsx(f,{name:u,uuid:l}):(console.warn("Invalid widget!",u),null)},p=t=>{const{root:r,container:n,svgSpritePath:o,children:s}=t,[i,a]=e.useState(!1),[l,f]=e.useState();return e.useEffect((()=>{o?(async e=>{try{const t=await fetch(`${e}`);if(!t.ok)throw new Error(`Failed to fetch SVG: ${t.statusText}`);const r=await t.text(),n=(new DOMParser).parseFromString(r,"image/svg+xml");if(n.querySelector("parsererror"))throw new Error("SVG parsing failed");const o=n.documentElement;return o.setAttribute("xmlns","http://www.w3.org/2000/svg"),o.removeAttribute("id"),o}catch(e){return console.error("Error generating SVG sprite:",e),null}})(o).then((async e=>{n&&e&&n.appendChild(e),a(!0)})):a(!0)}),[]),c("@@-widget-update-props",(e=>{const{widgetProps:t,uuid:n}=e;r.uuid===n&&f(t)})),i?e.cloneElement(s,{...l,key:performance.now()}):null},d=(e,t)=>{if(null===e||"object"!=typeof e)return e;const r=t.get(e);if(r)return r;if(Array.isArray(e)){const r=e.length;if(r<32){const n=new Array(r);t.set(e,n);for(let o=0;o<r;o++)o in e&&(n[o]=d(e[o],t));return n}const n=new Array(r);if(t.set(e,n),Object.keys(e).length===r){let o=0;const s=r-r%8;for(;o<s;)n[o]=d(e[o],t),n[o+1]=d(e[o+1],t),n[o+2]=d(e[o+2],t),n[o+3]=d(e[o+3],t),n[o+4]=d(e[o+4],t),n[o+5]=d(e[o+5],t),n[o+6]=d(e[o+6],t),n[o+7]=d(e[o+7],t),o+=8;for(;o<r;)n[o]=d(e[o],t),o++}else for(let o=0;o<r;o++)o in e&&(n[o]=d(e[o],t));return n}if(e instanceof Date||e instanceof RegExp){return new(0,e.constructor)(e)}if("undefined"!=typeof Buffer)try{if(Buffer.isBuffer(e))return Buffer.from(e)}catch(e){if(e instanceof Error)throw e}if(ArrayBuffer.isView(e)){const t=e.constructor;if("buffer"in e){const r=e;return new t(r.buffer.slice(r.byteOffset,r.byteOffset+r.byteLength))}return new t(new ArrayBuffer(e.byteLength))}if(e instanceof ArrayBuffer)return e.slice(0);if(e&&"object"==typeof e&&"buffer"in e&&"byteLength"in e){const t=e;if("undefined"==typeof Buffer){const e=new Uint8Array(t.buffer,t.byteOffset||0,t.byteLength),r=new Uint8Array(e.length);return r.set(e),r}return{buffer:t.buffer instanceof ArrayBuffer?t.buffer.slice(0):new ArrayBuffer(t.byteLength),byteLength:t.byteLength,byteOffset:t.byteOffset||0,length:t.length||t.byteLength,BYTES_PER_ELEMENT:t.BYTES_PER_ELEMENT||1}}if(e instanceof Set){const r=new Set;t.set(e,r);const n=Array.from(e),o=n.length;for(let e=0;e<o;e++)r.add(d(n[e],t));return r}if(e instanceof Map){const r=new Map;t.set(e,r);const n=Array.from(e),o=n.length;for(let e=0;e<o;e++){const[o,s]=n[e];r.set(o,d(s,t))}return r}if(Object.getPrototypeOf(e)===Object.prototype){const r={};t.set(e,r);const n=Object.getOwnPropertySymbols(e),o=n.length;if(o>0)for(let t=0;t<o;t++){const o=n[t],s=Object.getOwnPropertyDescriptor(e,o);s?.enumerable&&Object.defineProperty(r,o,s)}const s=Object.getOwnPropertyDescriptors(e),i=Object.keys(e),a=i.length;let c=!1;for(let e=0;e<a;e++){const t=s[i[e]];if(t.get||t.set){c=!0;break}}if(c)for(let e=0;e<a;e++){const n=i[e],o=s[n];if(o.enumerable)if(o.get||o.set)Object.defineProperty(r,n,o);else{const e=d(o.value,t);Object.defineProperty(r,n,{...o,value:e})}}else{let n=0;const o=a-a%8;for(;n<o;){const o=i[n],s=i[n+1],a=i[n+2],c=i[n+3],l=i[n+4],f=i[n+5],u=i[n+6],p=i[n+7],g=e;r[o]=d(g[o],t),r[s]=d(g[s],t),r[a]=d(g[a],t),r[c]=d(g[c],t),r[l]=d(g[l],t),r[f]=d(g[f],t),r[u]=d(g[u],t),r[p]=d(g[p],t),n+=8}for(;n<a;){const o=i[n++];r[o]=d(e[o],t)}}return r}const n=Object.getPrototypeOf(e),o=Object.create(n);t.set(e,o);const s=Object.getOwnPropertyDescriptors(e),i=Object.keys(s),a=i.length;for(let e=0;e<a;e++){const r=i[e],n=s[r];if(n.enumerable)if(n.get||n.set)Object.defineProperty(o,r,n);else{const e=d(n.value,t);Object.defineProperty(o,r,{...n,value:e})}}const c=Object.getOwnPropertySymbols(e),l=c.length;for(let t=0;t<l;t++){const r=c[t],n=Object.getOwnPropertyDescriptor(e,r);n?.enumerable&&Object.defineProperty(o,r,n)}return o},g=(e,t)=>{if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(!g(e[r],t[r]))return!1;return!0}if(e instanceof Set&&t instanceof Set){if(e.size!==t.size)return!1;for(const r of e)if(!t.has(r))return!1;return!0}if(e instanceof Map&&t instanceof Map){if(e.size!==t.size)return!1;for(const[r,n]of e)if(!t.has(r)||!g(n,t.get(r)))return!1;return!0}if(null!==e&&"object"==typeof e&&!Array.isArray(e)&&null!==t&&"object"==typeof t&&!Array.isArray(t)){const r=Object.keys(e),n=Object.keys(t);if(r.length!==n.length)return!1;for(const n of r)if(!g(e[n],t[n]))return!1;return!0}return e===t};exports.WidgetComponent=u,exports.busDispatch=a,exports.busSubscribe=c,exports.calculateRenderedTextWidth=(e,t,r=!1,n='Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"')=>{const o=r?e.toUpperCase():e,s=document.createElement("canvas").getContext("2d");return s.font=`${t}px ${n}`,s.measureText(o).width},exports.camelCase=e=>e.trim().toLowerCase().replace(/^[-_\s]+/,"").replace(/[-_\s]+(.)?/g,((e,t)=>t?t.toUpperCase():"")),exports.capitalize=e=>e.charAt(0).toUpperCase()+e.slice(1),exports.capitalizeFirstLetter=e=>e.charAt(0).toUpperCase()+e.slice(1),exports.checkVisibility=e=>{const t=e.getBoundingClientRect(),r=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(t.bottom<0||t.top-r>=0)},exports.deepClone=e=>d(e,new WeakMap),exports.deepMerge=o,exports.importWidgets=async e=>{const t=e.filter((e=>e?.length&&!document.querySelector(`script[src="${e}"]`)));(await Promise.allSettled(t.map((e=>(async e=>new Promise(((t,r)=>{const n=document.createElement("script");n.onload=()=>{t(!0)},n.onerror=()=>{const t=new Error(`Failed to load script: ${e}`);r(t)},n.type="module",n.src=e,document.head.appendChild(n)})))(e))))).forEach(((e,r)=>{"rejected"===e.status&&console.error(`Failed to load widget script ${t[r]}:`,e.reason)})),a("@@-widgets-loaded",!0)},exports.isBoolean=e=>"boolean"==typeof e,exports.isBrowser=()=>"undefined"!=typeof window,exports.isEqual=g,exports.isFunction=e=>"function"==typeof e,exports.isNull=e=>null===e,exports.isNumber=e=>"number"==typeof e,exports.isObject=n,exports.isString=e=>"string"==typeof e,exports.isUndefined=e=>void 0===e,exports.kebabCase=e=>null==e?e:e.normalize("NFKD").replace(/\p{Diacritic}/gu,"").replace(/[^\w\s-]/g," ").replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").replace(/[-_\s]+/g,"-").toLowerCase().replace(/^-+|-+$/g,"").trim(),exports.merge=(e,...t)=>Object.assign(e,...t),exports.omit=(e,t)=>t.reduce(((e,t)=>(delete e[t],e)),{...e}),exports.pascalCase=e=>e.split(/(?=[0-9])|(?<=[0-9])|[^a-zA-Z0-9]+/g).filter(Boolean).map((e=>e.toLowerCase())).map((e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`)).join(""),exports.pick=(e,t)=>{const r={};for(const n of t)n in e&&(r[n]=e[n]);return r},exports.r2wc=l,exports.registerWidget=e=>{const{name:r,styles:n,component:o,svgSpritePath:s}=e;return l(`widget-${r}`,(e=>{const{root:n,container:i,...a}=e;return t.jsx(p,{container:i,root:n,svgSpritePath:s,children:t.jsx(o,{...a,name:r})})}),{shadow:"open"},n)},exports.renderWidget=e=>{const{name:n,uuid:o,mountTo:s,widgetProps:i}=e,a=o?.trim()||crypto.randomUUID().replace(/-/g,"").slice(0,6),c=n.trim().toLowerCase();!!c.length&&!!a.length&&s&&s instanceof HTMLElement&&r.createRoot(s).render(t.jsx(u,{name:c,uuid:a,widgetProps:i}))},exports.setStyleProperties=(e,t)=>{if(e)for(const[r,n]of Object.entries(t))e.style.setProperty(r,n)},exports.sleep=e=>new Promise((t=>{setTimeout(t,e)})),exports.slugify=e=>e.normalize("NFKD").toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[-\s_]+/g,"-").replace(/^-+|-+$/g,""),exports.storageCalculateSize=async(e,t)=>{const r=await caches.open(e);if(t){const e=await r.match(t);if(e){const t=e.clone();return(await t.arrayBuffer()).byteLength}return 0}const n=await r.keys();let o=0;for(const e of n){const t=await r.match(e);if(t){const e=t.clone();o+=(await e.arrayBuffer()).byteLength}}return o},exports.storageClear=async e=>{const t=await caches.open(e),r=await t.keys();for(const e of r)await t.delete(e)},exports.storageClearByPrefixOrSuffix=async(e,t,r=!0)=>{const n=await caches.open(e),o=await n.keys();for(const e of o){const o=e.url.split("/"),s=o[o.length-1]||"";(r&&s.startsWith(t)||!r&&s.endsWith(t))&&await n.delete(e)}},exports.storageExists=async(e,t)=>{const r=await caches.open(e);return void 0!==await r.match(t)},exports.storageGetAllKeys=async e=>{const t=await caches.open(e);return(await t.keys()).map((e=>{const t=e.url.split("/");return t[t.length-1]||""}))},exports.storageGetItem=async(e,t)=>{const r=await caches.open(e),n=await r.match(t);if(!n)return null;const o=await n.text();return JSON.parse(o)},exports.storageRemoveItem=async(e,t)=>{const r=await caches.open(e);return await r.delete(t)},exports.storageSetItem=async(e,t,r)=>{const n=await caches.open(e),o=JSON.stringify(r,s),i=new Response(o,{headers:{"Content-Type":"application/json"}});await n.put(t,i)},exports.stringifyBigIntValues=s,exports.uncapitalize=e=>e.charAt(0).toLowerCase()+e.slice(1),exports.useEventBus=(t,r,n=[])=>{e.useEffect((()=>{if(t&&"function"==typeof r)return c(t,r)}),[t,r,...n])};
|
|
7
|
+
"use strict";var e=require("react");const t=e=>{if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t},r=(e,...n)=>{if(!n.length)return e;const s=n.shift();if(void 0===s)return e;if(t(e)&&t(s))for(const n of Object.keys(s)){const o=s[n];if(t(o)){e[n]||Object.assign(e,{[n]:{}});const s=e[n];t(s)&&r(s,o)}else Object.assign(e,{[n]:o})}return r(e,...n)},n=(e,t)=>"bigint"==typeof t?t.toString():t,s=e=>e.startsWith("http://")||e.startsWith("https://")?e:`https://cache.internal/${e}`,o=e=>`${((e,t="๐")=>{let r=0;const n=e+t;for(let e=0;e<n.length;e++)r=(r<<5)-r+n.charCodeAt(e),r|=0;return(r>>>0).toString(16)})(e)}::${e}`,i=(e,t)=>{if(!e||"function"!=typeof t)return()=>{};const r=(e=>t=>{if(t instanceof CustomEvent)try{e(t.detail)}catch(e){console.error("Event listener error:",e)}})(t),n=o(e);return window.addEventListener(n,r),()=>window.removeEventListener(n,r)},a=(e,t)=>{if(null===e||"object"!=typeof e)return e;const r=t.get(e);if(r)return r;if(Array.isArray(e)){const r=e.length;if(r<32){const n=new Array(r);t.set(e,n);for(let s=0;s<r;s++)s in e&&(n[s]=a(e[s],t));return n}const n=new Array(r);if(t.set(e,n),Object.keys(e).length===r){let s=0;const o=r-r%8;for(;s<o;)n[s]=a(e[s],t),n[s+1]=a(e[s+1],t),n[s+2]=a(e[s+2],t),n[s+3]=a(e[s+3],t),n[s+4]=a(e[s+4],t),n[s+5]=a(e[s+5],t),n[s+6]=a(e[s+6],t),n[s+7]=a(e[s+7],t),s+=8;for(;s<r;)n[s]=a(e[s],t),s++}else for(let s=0;s<r;s++)s in e&&(n[s]=a(e[s],t));return n}if(e instanceof Date||e instanceof RegExp){return new(0,e.constructor)(e)}if("undefined"!=typeof Buffer)try{if(Buffer.isBuffer(e))return Buffer.from(e)}catch(e){if(e instanceof Error)throw e}if(ArrayBuffer.isView(e)){const t=e.constructor;if("buffer"in e){const r=e;return new t(r.buffer.slice(r.byteOffset,r.byteOffset+r.byteLength))}return new t(new ArrayBuffer(e.byteLength))}if(e instanceof ArrayBuffer)return e.slice(0);if(e&&"object"==typeof e&&"buffer"in e&&"byteLength"in e){const t=e;if("undefined"==typeof Buffer){const e=new Uint8Array(t.buffer,t.byteOffset||0,t.byteLength),r=new Uint8Array(e.length);return r.set(e),r}return{buffer:t.buffer instanceof ArrayBuffer?t.buffer.slice(0):new ArrayBuffer(t.byteLength),byteLength:t.byteLength,byteOffset:t.byteOffset||0,length:t.length||t.byteLength,BYTES_PER_ELEMENT:t.BYTES_PER_ELEMENT||1}}if(e instanceof Set){const r=new Set;t.set(e,r);const n=Array.from(e),s=n.length;for(let e=0;e<s;e++)r.add(a(n[e],t));return r}if(e instanceof Map){const r=new Map;t.set(e,r);const n=Array.from(e),s=n.length;for(let e=0;e<s;e++){const[s,o]=n[e];r.set(s,a(o,t))}return r}if(Object.getPrototypeOf(e)===Object.prototype){const r={};t.set(e,r);const n=Object.getOwnPropertySymbols(e),s=n.length;if(s>0)for(let t=0;t<s;t++){const s=n[t],o=Object.getOwnPropertyDescriptor(e,s);o?.enumerable&&Object.defineProperty(r,s,o)}const o=Object.getOwnPropertyDescriptors(e),i=Object.keys(e),c=i.length;let f=!1;for(let e=0;e<c;e++){const t=o[i[e]];if(t.get||t.set){f=!0;break}}if(f)for(let e=0;e<c;e++){const n=i[e],s=o[n];if(s.enumerable)if(s.get||s.set)Object.defineProperty(r,n,s);else{const e=a(s.value,t);Object.defineProperty(r,n,{...s,value:e})}}else{let n=0;const s=c-c%8;for(;n<s;){const s=i[n],o=i[n+1],c=i[n+2],f=i[n+3],l=i[n+4],p=i[n+5],u=i[n+6],y=i[n+7],g=e;r[s]=a(g[s],t),r[o]=a(g[o],t),r[c]=a(g[c],t),r[f]=a(g[f],t),r[l]=a(g[l],t),r[p]=a(g[p],t),r[u]=a(g[u],t),r[y]=a(g[y],t),n+=8}for(;n<c;){const s=i[n++];r[s]=a(e[s],t)}}return r}const n=Object.getPrototypeOf(e),s=Object.create(n);t.set(e,s);const o=Object.getOwnPropertyDescriptors(e),i=Object.keys(o),c=i.length;for(let e=0;e<c;e++){const r=i[e],n=o[r];if(n.enumerable)if(n.get||n.set)Object.defineProperty(s,r,n);else{const e=a(n.value,t);Object.defineProperty(s,r,{...n,value:e})}}const f=Object.getOwnPropertySymbols(e),l=f.length;for(let t=0;t<l;t++){const r=f[t],n=Object.getOwnPropertyDescriptor(e,r);n?.enumerable&&Object.defineProperty(s,r,n)}return s},c=(e,t)=>{if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(!c(e[r],t[r]))return!1;return!0}if(e instanceof Set&&t instanceof Set){if(e.size!==t.size)return!1;for(const r of e)if(!t.has(r))return!1;return!0}if(e instanceof Map&&t instanceof Map){if(e.size!==t.size)return!1;for(const[r,n]of e)if(!t.has(r)||!c(n,t.get(r)))return!1;return!0}if(null!==e&&"object"==typeof e&&!Array.isArray(e)&&null!==t&&"object"==typeof t&&!Array.isArray(t)){const r=Object.keys(e),n=Object.keys(t);if(r.length!==n.length)return!1;for(const n of r)if(!c(e[n],t[n]))return!1;return!0}return e===t};exports.busDispatch=(e,t)=>{if(!e)return;const r=o(e);window.dispatchEvent(new CustomEvent(r,{detail:t,bubbles:!0,cancelable:!1}))},exports.busSubscribe=i,exports.calculateRenderedTextWidth=(e,t,r=!1,n='Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"')=>{const s=r?e.toUpperCase():e,o=document.createElement("canvas").getContext("2d");return o.font=`${t}px ${n}`,o.measureText(s).width},exports.camelCase=e=>e.trim().toLowerCase().replace(/^[-_\s]+/,"").replace(/[-_\s]+(.)?/g,((e,t)=>t?t.toUpperCase():"")),exports.capitalize=e=>e.charAt(0).toUpperCase()+e.slice(1),exports.capitalizeFirstLetter=e=>e.charAt(0).toUpperCase()+e.slice(1),exports.checkVisibility=e=>{const t=e.getBoundingClientRect(),r=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(t.bottom<0||t.top-r>=0)},exports.deepClone=e=>a(e,new WeakMap),exports.deepMerge=r,exports.isBoolean=e=>"boolean"==typeof e,exports.isBrowser=()=>"undefined"!=typeof window,exports.isEqual=c,exports.isFunction=e=>"function"==typeof e,exports.isNull=e=>null===e,exports.isNumber=e=>"number"==typeof e,exports.isObject=t,exports.isString=e=>"string"==typeof e,exports.isUndefined=e=>void 0===e,exports.kebabCase=e=>null==e?e:e.normalize("NFKD").replace(/\p{Diacritic}/gu,"").replace(/[^\w\s-]/g," ").replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").replace(/[-_\s]+/g,"-").toLowerCase().replace(/^-+|-+$/g,"").trim(),exports.merge=(e,...t)=>Object.assign(e,...t),exports.omit=(e,t)=>t.reduce(((e,t)=>(delete e[t],e)),{...e}),exports.pascalCase=e=>e.split(/(?=[0-9])|(?<=[0-9])|[^a-zA-Z0-9]+/g).filter(Boolean).map((e=>e.toLowerCase())).map((e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`)).join(""),exports.pick=(e,t)=>{const r={};for(const n of t)n in e&&(r[n]=e[n]);return r},exports.setStyleProperties=(e,t)=>{if(e)for(const[r,n]of Object.entries(t))e.style.setProperty(r,n)},exports.sleep=e=>new Promise((t=>{setTimeout(t,e)})),exports.slugify=e=>e.normalize("NFKD").toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[-\s_]+/g,"-").replace(/^-+|-+$/g,""),exports.storageCalculateSize=async(e,t)=>{const r=await caches.open(e);if(t){const e=s(t),n=await r.match(new Request(e));if(n){const e=n.clone();return(await e.arrayBuffer()).byteLength}return 0}const n=await r.keys();let o=0;for(const e of n){const t=await r.match(e);if(t){const e=t.clone();o+=(await e.arrayBuffer()).byteLength}}return o},exports.storageClear=async e=>{const t=await caches.open(e),r=await t.keys();for(const e of r)await t.delete(e)},exports.storageClearByPrefixOrSuffix=async(e,t,r=!0)=>{const n=await caches.open(e),s=await n.keys();for(const e of s){const s=e.url.split("/"),o=s[s.length-1]||"";(r&&o.startsWith(t)||!r&&o.endsWith(t))&&await n.delete(e)}},exports.storageExists=async(e,t)=>{const r=await caches.open(e),n=s(t);return void 0!==await r.match(new Request(n))},exports.storageGetAllKeys=async e=>{const t=await caches.open(e);return(await t.keys()).map((e=>{const t=e.url.split("/");return t[t.length-1]||""}))},exports.storageGetItem=async(e,t)=>{const r=await caches.open(e),n=s(t),o=await r.match(new Request(n));if(!o)return null;const i=await o.text();return JSON.parse(i)},exports.storageRemoveItem=async(e,t)=>{const r=await caches.open(e),n=s(t);return await r.delete(new Request(n))},exports.storageSetItem=async(e,t,r)=>{const o=await caches.open(e),i=JSON.stringify(r,n),a=new Response(i,{headers:{"Content-Type":"application/json"}}),c=s(t);await o.put(new Request(c),a)},exports.stringifyBigIntValues=n,exports.uncapitalize=e=>e.charAt(0).toLowerCase()+e.slice(1),exports.useEventBus=(t,r,n=[])=>{e.useEffect((()=>{if(t&&"function"==typeof r)return i(t,r)}),[t,r,...n])};
|
package/dist/esm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @pivanov/utils v0.0.
|
|
2
|
+
* @pivanov/utils v0.0.3
|
|
3
3
|
* (c) 2024-present Pavel Ivanov
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
* https://github.com/pivanov/utils
|
|
6
6
|
*/
|
|
7
|
-
import{useEffect as e,memo as t,useRef as r,useState as n,cloneElement as o}from"react";import{jsx as s}from"react/jsx-runtime";import{createRoot as i}from"react-dom/client";const c=e=>"boolean"==typeof e,a=e=>"number"==typeof e,f=e=>"string"==typeof e,l=e=>"function"==typeof e,u=e=>{if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t},p=e=>void 0===e,d=e=>null===e,h=(e,t)=>t.reduce(((e,t)=>(delete e[t],e)),{...e}),g=(e,t)=>{const r={};for(const n of t)n in e&&(r[n]=e[n]);return r},y=(e,...t)=>Object.assign(e,...t),w=(e,...t)=>{if(!t.length)return e;const r=t.shift();if(void 0===r)return e;if(u(e)&&u(r))for(const t of Object.keys(r)){const n=r[t];if(u(n)){e[t]||Object.assign(e,{[t]:{}});const r=e[t];u(r)&&w(r,n)}else Object.assign(e,{[t]:n})}return w(e,...t)},m=e=>new Promise((t=>{setTimeout(t,e)})),b=e=>e.trim().toLowerCase().replace(/^[-_\s]+/,"").replace(/[-_\s]+(.)?/g,((e,t)=>t?t.toUpperCase():"")),O=e=>e.split(/(?=[0-9])|(?<=[0-9])|[^a-zA-Z0-9]+/g).filter(Boolean).map((e=>e.toLowerCase())).map((e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`)).join(""),j=e=>e.charAt(0).toUpperCase()+e.slice(1),E=e=>null==e?e:e.normalize("NFKD").replace(/\p{Diacritic}/gu,"").replace(/[^\w\s-]/g," ").replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").replace(/[-_\s]+/g,"-").toLowerCase().replace(/^-+|-+$/g,"").trim(),A=e=>e.normalize("NFKD").toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[-\s_]+/g,"-").replace(/^-+|-+$/g,""),P=e=>e.charAt(0).toUpperCase()+e.slice(1),C=e=>e.charAt(0).toLowerCase()+e.slice(1),S=(e,t)=>"bigint"==typeof t?t.toString():t,L=async(e,t,r)=>{const n=await caches.open(e),o=JSON.stringify(r,S),s=new Response(o,{headers:{"Content-Type":"application/json"}});await n.put(t,s)},$=async(e,t)=>{const r=await caches.open(e),n=await r.match(t);if(!n)return null;const o=await n.text();return JSON.parse(o)},v=async(e,t)=>{const r=await caches.open(e);return await r.delete(t)},k=async e=>{const t=await caches.open(e),r=await t.keys();for(const e of r)await t.delete(e)},B=async(e,t,r=!0)=>{const n=await caches.open(e),o=await n.keys();for(const e of o){const o=e.url.split("/"),s=o[o.length-1]||"";(r&&s.startsWith(t)||!r&&s.endsWith(t))&&await n.delete(e)}},x=async(e,t)=>{const r=await caches.open(e);return void 0!==await r.match(t)},T=async e=>{const t=await caches.open(e);return(await t.keys()).map((e=>{const t=e.url.split("/");return t[t.length-1]||""}))},D=async(e,t)=>{const r=await caches.open(e);if(t){const e=await r.match(t);if(e){const t=e.clone();return(await t.arrayBuffer()).byteLength}return 0}const n=await r.keys();let o=0;for(const e of n){const t=await r.match(e);if(t){const e=t.clone();o+=(await e.arrayBuffer()).byteLength}}return o},M=e=>`${((e,t="๐")=>{let r=0;const n=e+t;for(let e=0;e<n.length;e++)r=(r<<5)-r+n.charCodeAt(e),r|=0;return(r>>>0).toString(16)})(e)}::${e}`,U=(e,t)=>{if(!e)return;const r=M(e);window.dispatchEvent(new CustomEvent(r,{detail:t,bubbles:!0,cancelable:!1}))},N=(e,t)=>{if(!e||"function"!=typeof t)return()=>{};const r=(e=>t=>{if(t instanceof CustomEvent)try{e(t.detail)}catch(e){console.error("Event listener error:",e)}})(t),n=M(e);return window.addEventListener(n,r),()=>window.removeEventListener(n,r)},z=(t,r,n=[])=>{e((()=>{if(t&&"function"==typeof r)return N(t,r)}),[t,r,...n])},K=(e,t,r={},n)=>{const o=class extends HTMLElement{constructor(){super();let e=Array.from(document.styleSheets).find((e=>e instanceof CSSStyleSheet&&!e.disabled));if(!e){const t=document.createElement("style");document.head.appendChild(t),e=t.sheet}Array.from(e.cssRules).find((e=>e.selectorText===`${this.tagName.toLowerCase()}`))||e.insertRule(`${this.tagName.toLowerCase()} { display: inline-flex; }`,e.cssRules.length);const t=r.shadow||(n?.length?"open":"closed");if(!r.shadow&&n?.length&&console.info("@@@ Styles are provided but shadowDOM is not enabled"),this.container=t?this.attachShadow({mode:t}):this,n&&t){const e=document.createElement("style");e.textContent=n.join("\n"),this.container.appendChild(e)}}connectedCallback(){this.root||(this.root=i(this.container),this.subscribeToEvents())}disconnectedCallback(){this.root&&this.root.unmount()}subscribeToEvents(){N("@@-widget-create",(e=>{const{widgetProps:t,cacheKey:r}=e;this.cacheKey||r!==`${this.tagName.toLowerCase().replace("widget-","")}-${this.uuid}`||(this.cacheKey=r,this.mountComponent(t))})),N("@@-widget-update",(e=>{const{widgetProps:t,cacheKey:r}=e;this.cacheKey===r&&r===`${this.tagName.toLowerCase().replace("widget-","")}-${this.uuid}`&&U("@@-widget-update-props",{widgetProps:t,uuid:this.uuid})}))}mountComponent(e){this.root&&e&&this.root.render(s(t,{...e,container:this.container,root:this}))}};return customElements.get(e)||customElements.define(e,o),o},R=t((t=>{const{name:n,uuid:o}=t,i=r(),c=`widget-${n}`;return e((()=>{i.current&&(i.current.uuid=o)}),[o]),"string"==typeof o&&o.length?s(c,{ref:i}):null}),(()=>!0)),_=t=>{const{name:n,uuid:o,widgetProps:i}=t,c=r(!1),a=o?.trim()||crypto.randomUUID().replace(/-/g,"").slice(0,6),f=n.trim().toLowerCase(),l=`${f}-${a}`,u=!!f.length&&!!a.length;return e((()=>{u&&c.current&&U("@@-widget-update",{widgetName:f,widgetProps:i,cacheKey:l})}),[i,a,f,u,l]),N("@@-widgets-loaded",(()=>{c.current=!0,U("@@-widget-create",{widgetName:f,widgetProps:i,cacheKey:l})})),u?s(R,{name:f,uuid:a}):(console.warn("Invalid widget!",f),null)},F=e=>{const{name:t,uuid:r,mountTo:n,widgetProps:o}=e,c=r?.trim()||crypto.randomUUID().replace(/-/g,"").slice(0,6),a=t.trim().toLowerCase();!!a.length&&!!c.length&&n&&n instanceof HTMLElement&&i(n).render(s(_,{name:a,uuid:c,widgetProps:o}))},I=t=>{const{root:r,container:s,svgSpritePath:i,children:c}=t,[a,f]=n(!1),[l,u]=n();return e((()=>{i?(async e=>{try{const t=await fetch(`${e}`);if(!t.ok)throw new Error(`Failed to fetch SVG: ${t.statusText}`);const r=await t.text(),n=(new DOMParser).parseFromString(r,"image/svg+xml");if(n.querySelector("parsererror"))throw new Error("SVG parsing failed");const o=n.documentElement;return o.setAttribute("xmlns","http://www.w3.org/2000/svg"),o.removeAttribute("id"),o}catch(e){return console.error("Error generating SVG sprite:",e),null}})(i).then((async e=>{s&&e&&s.appendChild(e),f(!0)})):f(!0)}),[]),N("@@-widget-update-props",(e=>{const{widgetProps:t,uuid:n}=e;r.uuid===n&&u(t)})),a?o(c,{...l,key:performance.now()}):null},H=e=>{const{name:t,styles:r,component:n,svgSpritePath:o}=e;return K(`widget-${t}`,(e=>{const{root:r,container:i,...c}=e;return s(I,{container:i,root:r,svgSpritePath:o,children:s(n,{...c,name:t})})}),{shadow:"open"},r)},V=async e=>{const t=e.filter((e=>e?.length&&!document.querySelector(`script[src="${e}"]`)));(await Promise.allSettled(t.map((e=>(async e=>new Promise(((t,r)=>{const n=document.createElement("script");n.onload=()=>{t(!0)},n.onerror=()=>{const t=new Error(`Failed to load script: ${e}`);r(t)},n.type="module",n.src=e,document.head.appendChild(n)})))(e))))).forEach(((e,r)=>{"rejected"===e.status&&console.error(`Failed to load widget script ${t[r]}:`,e.reason)})),U("@@-widgets-loaded",!0)},Z=e=>G(e,new WeakMap),G=(e,t)=>{if(null===e||"object"!=typeof e)return e;const r=t.get(e);if(r)return r;if(Array.isArray(e)){const r=e.length;if(r<32){const n=new Array(r);t.set(e,n);for(let o=0;o<r;o++)o in e&&(n[o]=G(e[o],t));return n}const n=new Array(r);if(t.set(e,n),Object.keys(e).length===r){let o=0;const s=r-r%8;for(;o<s;)n[o]=G(e[o],t),n[o+1]=G(e[o+1],t),n[o+2]=G(e[o+2],t),n[o+3]=G(e[o+3],t),n[o+4]=G(e[o+4],t),n[o+5]=G(e[o+5],t),n[o+6]=G(e[o+6],t),n[o+7]=G(e[o+7],t),o+=8;for(;o<r;)n[o]=G(e[o],t),o++}else for(let o=0;o<r;o++)o in e&&(n[o]=G(e[o],t));return n}if(e instanceof Date||e instanceof RegExp){return new(0,e.constructor)(e)}if("undefined"!=typeof Buffer)try{if(Buffer.isBuffer(e))return Buffer.from(e)}catch(e){if(e instanceof Error)throw e}if(ArrayBuffer.isView(e)){const t=e.constructor;if("buffer"in e){const r=e;return new t(r.buffer.slice(r.byteOffset,r.byteOffset+r.byteLength))}return new t(new ArrayBuffer(e.byteLength))}if(e instanceof ArrayBuffer)return e.slice(0);if(e&&"object"==typeof e&&"buffer"in e&&"byteLength"in e){const t=e;if("undefined"==typeof Buffer){const e=new Uint8Array(t.buffer,t.byteOffset||0,t.byteLength),r=new Uint8Array(e.length);return r.set(e),r}return{buffer:t.buffer instanceof ArrayBuffer?t.buffer.slice(0):new ArrayBuffer(t.byteLength),byteLength:t.byteLength,byteOffset:t.byteOffset||0,length:t.length||t.byteLength,BYTES_PER_ELEMENT:t.BYTES_PER_ELEMENT||1}}if(e instanceof Set){const r=new Set;t.set(e,r);const n=Array.from(e),o=n.length;for(let e=0;e<o;e++)r.add(G(n[e],t));return r}if(e instanceof Map){const r=new Map;t.set(e,r);const n=Array.from(e),o=n.length;for(let e=0;e<o;e++){const[o,s]=n[e];r.set(o,G(s,t))}return r}if(Object.getPrototypeOf(e)===Object.prototype){const r={};t.set(e,r);const n=Object.getOwnPropertySymbols(e),o=n.length;if(o>0)for(let t=0;t<o;t++){const o=n[t],s=Object.getOwnPropertyDescriptor(e,o);s?.enumerable&&Object.defineProperty(r,o,s)}const s=Object.getOwnPropertyDescriptors(e),i=Object.keys(e),c=i.length;let a=!1;for(let e=0;e<c;e++){const t=s[i[e]];if(t.get||t.set){a=!0;break}}if(a)for(let e=0;e<c;e++){const n=i[e],o=s[n];if(o.enumerable)if(o.get||o.set)Object.defineProperty(r,n,o);else{const e=G(o.value,t);Object.defineProperty(r,n,{...o,value:e})}}else{let n=0;const o=c-c%8;for(;n<o;){const o=i[n],s=i[n+1],c=i[n+2],a=i[n+3],f=i[n+4],l=i[n+5],u=i[n+6],p=i[n+7],d=e;r[o]=G(d[o],t),r[s]=G(d[s],t),r[c]=G(d[c],t),r[a]=G(d[a],t),r[f]=G(d[f],t),r[l]=G(d[l],t),r[u]=G(d[u],t),r[p]=G(d[p],t),n+=8}for(;n<c;){const o=i[n++];r[o]=G(e[o],t)}}return r}const n=Object.getPrototypeOf(e),o=Object.create(n);t.set(e,o);const s=Object.getOwnPropertyDescriptors(e),i=Object.keys(s),c=i.length;for(let e=0;e<c;e++){const r=i[e],n=s[r];if(n.enumerable)if(n.get||n.set)Object.defineProperty(o,r,n);else{const e=G(n.value,t);Object.defineProperty(o,r,{...n,value:e})}}const a=Object.getOwnPropertySymbols(e),f=a.length;for(let t=0;t<f;t++){const r=a[t],n=Object.getOwnPropertyDescriptor(e,r);n?.enumerable&&Object.defineProperty(o,r,n)}return o},W=()=>"undefined"!=typeof window,q=(e,t)=>{if(e)for(const[r,n]of Object.entries(t))e.style.setProperty(r,n)},J=e=>{const t=e.getBoundingClientRect(),r=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(t.bottom<0||t.top-r>=0)},Y=(e,t,r=!1,n='Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"')=>{const o=r?e.toUpperCase():e,s=document.createElement("canvas").getContext("2d");return s.font=`${t}px ${n}`,s.measureText(o).width},Q=(e,t)=>{if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(!Q(e[r],t[r]))return!1;return!0}if(e instanceof Set&&t instanceof Set){if(e.size!==t.size)return!1;for(const r of e)if(!t.has(r))return!1;return!0}if(e instanceof Map&&t instanceof Map){if(e.size!==t.size)return!1;for(const[r,n]of e)if(!t.has(r)||!Q(n,t.get(r)))return!1;return!0}if(null!==e&&"object"==typeof e&&!Array.isArray(e)&&null!==t&&"object"==typeof t&&!Array.isArray(t)){const r=Object.keys(e),n=Object.keys(t);if(r.length!==n.length)return!1;for(const n of r)if(!Q(e[n],t[n]))return!1;return!0}return e===t};export{_ as WidgetComponent,U as busDispatch,N as busSubscribe,Y as calculateRenderedTextWidth,b as camelCase,P as capitalize,j as capitalizeFirstLetter,J as checkVisibility,Z as deepClone,w as deepMerge,V as importWidgets,c as isBoolean,W as isBrowser,Q as isEqual,l as isFunction,d as isNull,a as isNumber,u as isObject,f as isString,p as isUndefined,E as kebabCase,y as merge,h as omit,O as pascalCase,g as pick,K as r2wc,H as registerWidget,F as renderWidget,q as setStyleProperties,m as sleep,A as slugify,D as storageCalculateSize,k as storageClear,B as storageClearByPrefixOrSuffix,x as storageExists,T as storageGetAllKeys,$ as storageGetItem,v as storageRemoveItem,L as storageSetItem,S as stringifyBigIntValues,C as uncapitalize,z as useEventBus};
|
|
7
|
+
import{useEffect as e}from"react";const t=e=>"boolean"==typeof e,r=e=>"number"==typeof e,n=e=>"string"==typeof e,o=e=>"function"==typeof e,s=e=>{if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t},c=e=>void 0===e,a=e=>null===e,i=(e,t)=>t.reduce(((e,t)=>(delete e[t],e)),{...e}),f=(e,t)=>{const r={};for(const n of t)n in e&&(r[n]=e[n]);return r},l=(e,...t)=>Object.assign(e,...t),u=(e,...t)=>{if(!t.length)return e;const r=t.shift();if(void 0===r)return e;if(s(e)&&s(r))for(const t of Object.keys(r)){const n=r[t];if(s(n)){e[t]||Object.assign(e,{[t]:{}});const r=e[t];s(r)&&u(r,n)}else Object.assign(e,{[t]:n})}return u(e,...t)},p=e=>new Promise((t=>{setTimeout(t,e)})),y=e=>e.trim().toLowerCase().replace(/^[-_\s]+/,"").replace(/[-_\s]+(.)?/g,((e,t)=>t?t.toUpperCase():"")),g=e=>e.split(/(?=[0-9])|(?<=[0-9])|[^a-zA-Z0-9]+/g).filter(Boolean).map((e=>e.toLowerCase())).map((e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`)).join(""),b=e=>e.charAt(0).toUpperCase()+e.slice(1),w=e=>null==e?e:e.normalize("NFKD").replace(/\p{Diacritic}/gu,"").replace(/[^\w\s-]/g," ").replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").replace(/[-_\s]+/g,"-").toLowerCase().replace(/^-+|-+$/g,"").trim(),h=e=>e.normalize("NFKD").toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[-\s_]+/g,"-").replace(/^-+|-+$/g,""),O=e=>e.charAt(0).toUpperCase()+e.slice(1),m=e=>e.charAt(0).toLowerCase()+e.slice(1),d=(e,t)=>"bigint"==typeof t?t.toString():t,j=e=>e.startsWith("http://")||e.startsWith("https://")?e:`https://cache.internal/${e}`,A=async(e,t,r)=>{const n=await caches.open(e),o=JSON.stringify(r,d),s=new Response(o,{headers:{"Content-Type":"application/json"}}),c=j(t);await n.put(new Request(c),s)},E=async(e,t)=>{const r=await caches.open(e),n=j(t),o=await r.match(new Request(n));if(!o)return null;const s=await o.text();return JSON.parse(s)},L=async(e,t)=>{const r=await caches.open(e),n=j(t);return await r.delete(new Request(n))},P=async e=>{const t=await caches.open(e),r=await t.keys();for(const e of r)await t.delete(e)},B=async(e,t,r=!0)=>{const n=await caches.open(e),o=await n.keys();for(const e of o){const o=e.url.split("/"),s=o[o.length-1]||"";(r&&s.startsWith(t)||!r&&s.endsWith(t))&&await n.delete(e)}},C=async(e,t)=>{const r=await caches.open(e),n=j(t);return void 0!==await r.match(new Request(n))},v=async e=>{const t=await caches.open(e);return(await t.keys()).map((e=>{const t=e.url.split("/");return t[t.length-1]||""}))},S=async(e,t)=>{const r=await caches.open(e);if(t){const e=j(t),n=await r.match(new Request(e));if(n){const e=n.clone();return(await e.arrayBuffer()).byteLength}return 0}const n=await r.keys();let o=0;for(const e of n){const t=await r.match(e);if(t){const e=t.clone();o+=(await e.arrayBuffer()).byteLength}}return o},k=e=>`${((e,t="๐")=>{let r=0;const n=e+t;for(let e=0;e<n.length;e++)r=(r<<5)-r+n.charCodeAt(e),r|=0;return(r>>>0).toString(16)})(e)}::${e}`,$=(e,t)=>{if(!e)return;const r=k(e);window.dispatchEvent(new CustomEvent(r,{detail:t,bubbles:!0,cancelable:!1}))},D=(e,t)=>{if(!e||"function"!=typeof t)return()=>{};const r=(e=>t=>{if(t instanceof CustomEvent)try{e(t.detail)}catch(e){console.error("Event listener error:",e)}})(t),n=k(e);return window.addEventListener(n,r),()=>window.removeEventListener(n,r)},R=(t,r,n=[])=>{e((()=>{if(t&&"function"==typeof r)return D(t,r)}),[t,r,...n])},z=e=>M(e,new WeakMap),M=(e,t)=>{if(null===e||"object"!=typeof e)return e;const r=t.get(e);if(r)return r;if(Array.isArray(e)){const r=e.length;if(r<32){const n=new Array(r);t.set(e,n);for(let o=0;o<r;o++)o in e&&(n[o]=M(e[o],t));return n}const n=new Array(r);if(t.set(e,n),Object.keys(e).length===r){let o=0;const s=r-r%8;for(;o<s;)n[o]=M(e[o],t),n[o+1]=M(e[o+1],t),n[o+2]=M(e[o+2],t),n[o+3]=M(e[o+3],t),n[o+4]=M(e[o+4],t),n[o+5]=M(e[o+5],t),n[o+6]=M(e[o+6],t),n[o+7]=M(e[o+7],t),o+=8;for(;o<r;)n[o]=M(e[o],t),o++}else for(let o=0;o<r;o++)o in e&&(n[o]=M(e[o],t));return n}if(e instanceof Date||e instanceof RegExp){return new(0,e.constructor)(e)}if("undefined"!=typeof Buffer)try{if(Buffer.isBuffer(e))return Buffer.from(e)}catch(e){if(e instanceof Error)throw e}if(ArrayBuffer.isView(e)){const t=e.constructor;if("buffer"in e){const r=e;return new t(r.buffer.slice(r.byteOffset,r.byteOffset+r.byteLength))}return new t(new ArrayBuffer(e.byteLength))}if(e instanceof ArrayBuffer)return e.slice(0);if(e&&"object"==typeof e&&"buffer"in e&&"byteLength"in e){const t=e;if("undefined"==typeof Buffer){const e=new Uint8Array(t.buffer,t.byteOffset||0,t.byteLength),r=new Uint8Array(e.length);return r.set(e),r}return{buffer:t.buffer instanceof ArrayBuffer?t.buffer.slice(0):new ArrayBuffer(t.byteLength),byteLength:t.byteLength,byteOffset:t.byteOffset||0,length:t.length||t.byteLength,BYTES_PER_ELEMENT:t.BYTES_PER_ELEMENT||1}}if(e instanceof Set){const r=new Set;t.set(e,r);const n=Array.from(e),o=n.length;for(let e=0;e<o;e++)r.add(M(n[e],t));return r}if(e instanceof Map){const r=new Map;t.set(e,r);const n=Array.from(e),o=n.length;for(let e=0;e<o;e++){const[o,s]=n[e];r.set(o,M(s,t))}return r}if(Object.getPrototypeOf(e)===Object.prototype){const r={};t.set(e,r);const n=Object.getOwnPropertySymbols(e),o=n.length;if(o>0)for(let t=0;t<o;t++){const o=n[t],s=Object.getOwnPropertyDescriptor(e,o);s?.enumerable&&Object.defineProperty(r,o,s)}const s=Object.getOwnPropertyDescriptors(e),c=Object.keys(e),a=c.length;let i=!1;for(let e=0;e<a;e++){const t=s[c[e]];if(t.get||t.set){i=!0;break}}if(i)for(let e=0;e<a;e++){const n=c[e],o=s[n];if(o.enumerable)if(o.get||o.set)Object.defineProperty(r,n,o);else{const e=M(o.value,t);Object.defineProperty(r,n,{...o,value:e})}}else{let n=0;const o=a-a%8;for(;n<o;){const o=c[n],s=c[n+1],a=c[n+2],i=c[n+3],f=c[n+4],l=c[n+5],u=c[n+6],p=c[n+7],y=e;r[o]=M(y[o],t),r[s]=M(y[s],t),r[a]=M(y[a],t),r[i]=M(y[i],t),r[f]=M(y[f],t),r[l]=M(y[l],t),r[u]=M(y[u],t),r[p]=M(y[p],t),n+=8}for(;n<a;){const o=c[n++];r[o]=M(e[o],t)}}return r}const n=Object.getPrototypeOf(e),o=Object.create(n);t.set(e,o);const s=Object.getOwnPropertyDescriptors(e),c=Object.keys(s),a=c.length;for(let e=0;e<a;e++){const r=c[e],n=s[r];if(n.enumerable)if(n.get||n.set)Object.defineProperty(o,r,n);else{const e=M(n.value,t);Object.defineProperty(o,r,{...n,value:e})}}const i=Object.getOwnPropertySymbols(e),f=i.length;for(let t=0;t<f;t++){const r=i[t],n=Object.getOwnPropertyDescriptor(e,r);n?.enumerable&&Object.defineProperty(o,r,n)}return o},T=()=>"undefined"!=typeof window,U=(e,t)=>{if(e)for(const[r,n]of Object.entries(t))e.style.setProperty(r,n)},_=e=>{const t=e.getBoundingClientRect(),r=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(t.bottom<0||t.top-r>=0)},x=(e,t,r=!1,n='Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"')=>{const o=r?e.toUpperCase():e,s=document.createElement("canvas").getContext("2d");return s.font=`${t}px ${n}`,s.measureText(o).width},N=(e,t)=>{if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(!N(e[r],t[r]))return!1;return!0}if(e instanceof Set&&t instanceof Set){if(e.size!==t.size)return!1;for(const r of e)if(!t.has(r))return!1;return!0}if(e instanceof Map&&t instanceof Map){if(e.size!==t.size)return!1;for(const[r,n]of e)if(!t.has(r)||!N(n,t.get(r)))return!1;return!0}if(null!==e&&"object"==typeof e&&!Array.isArray(e)&&null!==t&&"object"==typeof t&&!Array.isArray(t)){const r=Object.keys(e),n=Object.keys(t);if(r.length!==n.length)return!1;for(const n of r)if(!N(e[n],t[n]))return!1;return!0}return e===t};export{$ as busDispatch,D as busSubscribe,x as calculateRenderedTextWidth,y as camelCase,O as capitalize,b as capitalizeFirstLetter,_ as checkVisibility,z as deepClone,u as deepMerge,t as isBoolean,T as isBrowser,N as isEqual,o as isFunction,a as isNull,r as isNumber,s as isObject,n as isString,c as isUndefined,w as kebabCase,l as merge,i as omit,g as pascalCase,f as pick,U as setStyleProperties,p as sleep,h as slugify,S as storageCalculateSize,P as storageClear,B as storageClearByPrefixOrSuffix,C as storageExists,v as storageGetAllKeys,E as storageGetItem,L as storageRemoveItem,A as storageSetItem,d as stringifyBigIntValues,m as uncapitalize,R as useEventBus};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @pivanov/utils v0.0.
|
|
2
|
+
* @pivanov/utils v0.0.3
|
|
3
3
|
* (c) 2024-present Pavel Ivanov
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
* https://github.com/pivanov/utils
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { DependencyList
|
|
9
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
8
|
+
import { DependencyList } from 'react';
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Type guard to check if a value is a boolean
|
|
@@ -598,131 +597,6 @@ declare const busSubscribe: <T extends IEventBus>(topic: IEventBus["topic"], lis
|
|
|
598
597
|
|
|
599
598
|
declare const useEventBus: <T extends IEventBus>(topic: T["topic"], listener: TEventBusListener<T["message"]>, deps?: DependencyList) => void;
|
|
600
599
|
|
|
601
|
-
declare global {
|
|
602
|
-
interface HTMLElement {
|
|
603
|
-
uuid?: string;
|
|
604
|
-
}
|
|
605
|
-
interface ISharedValues {
|
|
606
|
-
baseStoreUI: unknown;
|
|
607
|
-
}
|
|
608
|
-
interface Window {
|
|
609
|
-
pivanov?: unknown;
|
|
610
|
-
}
|
|
611
|
-
interface Navigator {
|
|
612
|
-
[key: string | symbol]: unknown;
|
|
613
|
-
}
|
|
614
|
-
interface Document {
|
|
615
|
-
[key: string | symbol]: unknown;
|
|
616
|
-
}
|
|
617
|
-
namespace JSX {
|
|
618
|
-
interface IntrinsicElements {
|
|
619
|
-
[elementName: `${string}-${string}`]: DetailedHTMLProps<HTMLAttributes<HTMLElement> & {
|
|
620
|
-
[key: string]: unknown;
|
|
621
|
-
}, HTMLElement>;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
interface IR2WCOptions {
|
|
626
|
-
shadow?: 'open' | 'closed';
|
|
627
|
-
}
|
|
628
|
-
interface IR2WCBaseProps {
|
|
629
|
-
container?: HTMLElement;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
/**
|
|
633
|
-
* Converts a React component into a Web Component (Custom Element)
|
|
634
|
-
*
|
|
635
|
-
* @template Props - The props type for the React component, must extend IR2WCBaseProps
|
|
636
|
-
* @param ReactComponent - The React component to convert
|
|
637
|
-
* @param options - Configuration options for the Web Component
|
|
638
|
-
* @param styles - Optional array of CSS styles to be injected into the shadow DOM
|
|
639
|
-
* @returns A Custom Element constructor that can be registered with customElements.define
|
|
640
|
-
*
|
|
641
|
-
* @example
|
|
642
|
-
* ```tsx
|
|
643
|
-
* const MyWebComponent = r2wc(MyReactComponent, {
|
|
644
|
-
* shadow: 'open',
|
|
645
|
-
* });
|
|
646
|
-
*
|
|
647
|
-
* customElements.define('my-component', MyWebComponent);
|
|
648
|
-
* ```
|
|
649
|
-
*/
|
|
650
|
-
declare const r2wc: <Props extends IR2WCBaseProps>(elementName: string, ReactComponent: ComponentType<Props>, options?: IR2WCOptions, styles?: string[]) => CustomElementConstructor;
|
|
651
|
-
|
|
652
|
-
/**
|
|
653
|
-
* Props interface for Widget components
|
|
654
|
-
* @interface IWidgetComponentProps
|
|
655
|
-
* @property {string} name - The name of the widget
|
|
656
|
-
* @property {string} [uuid] - Optional group identifier. Widgets sharing the same uuid will update together
|
|
657
|
-
* @property {string[]} [jsFiles] - Array of JavaScript file URLs to load. Files are loaded sequentially in the specified order, useful for dependencies
|
|
658
|
-
* @property {unknown} [widgetProps] - Optional props to pass to the widget
|
|
659
|
-
*/
|
|
660
|
-
interface IWidgetComponentProps {
|
|
661
|
-
name: string;
|
|
662
|
-
uuid?: string;
|
|
663
|
-
jsFiles?: string[];
|
|
664
|
-
widgetProps?: unknown;
|
|
665
|
-
}
|
|
666
|
-
/**
|
|
667
|
-
* React component that handles widget lifecycle and rendering
|
|
668
|
-
* @component
|
|
669
|
-
* @example
|
|
670
|
-
* ```tsx
|
|
671
|
-
* <WidgetComponent
|
|
672
|
-
* name="my-widget"
|
|
673
|
-
* uuid="group1" // Widgets with the same uuid will update together
|
|
674
|
-
* widgetProps={{ color: 'blue' }}
|
|
675
|
-
* />
|
|
676
|
-
* ```
|
|
677
|
-
* Multiple widgets with the same uuid will form a group - when one widget's props
|
|
678
|
-
* are updated, all widgets in the group will receive the update.
|
|
679
|
-
*/
|
|
680
|
-
declare const WidgetComponent: (props: IWidgetComponentProps) => react_jsx_runtime.JSX.Element | null;
|
|
681
|
-
/**
|
|
682
|
-
* Props interface for renderWidget function
|
|
683
|
-
* @interface IRenderWidgetProps
|
|
684
|
-
* @property {string} name - The name of the widget to load
|
|
685
|
-
* @property {string} [uuid] - Optional unique identifier for the widget
|
|
686
|
-
* @property {HTMLElement} mountTo - DOM element where the widget should be mounted
|
|
687
|
-
* @property {Object.<string, unknown>} widgetProps - Props to pass to the widget
|
|
688
|
-
*/
|
|
689
|
-
interface IRenderWidgetProps {
|
|
690
|
-
name: string;
|
|
691
|
-
uuid?: string;
|
|
692
|
-
mountTo: HTMLElement;
|
|
693
|
-
widgetProps?: {
|
|
694
|
-
[key: string]: unknown;
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
|
-
/**
|
|
698
|
-
* Programmatically loads and mounts a widget into a specified DOM element
|
|
699
|
-
* @function
|
|
700
|
-
* @param {IRenderWidgetProps} widgetProps - Configuration options for loading the widget
|
|
701
|
-
* @example
|
|
702
|
-
* ```ts
|
|
703
|
-
* renderWidget({
|
|
704
|
-
* widgetName: 'my-widget',
|
|
705
|
-
* uuid: 'group1',
|
|
706
|
-
* mountTo: document.getElementById('widget-container'),
|
|
707
|
-
* widgetProps: {
|
|
708
|
-
* color: 'blue',
|
|
709
|
-
* size: 'large'
|
|
710
|
-
* }
|
|
711
|
-
* });
|
|
712
|
-
* ```
|
|
713
|
-
*/
|
|
714
|
-
declare const renderWidget: (props: IRenderWidgetProps) => void;
|
|
715
|
-
|
|
716
|
-
interface IRegisterWidgetProps<T> {
|
|
717
|
-
name: string;
|
|
718
|
-
styles?: string[];
|
|
719
|
-
component: ComponentType<T>;
|
|
720
|
-
svgSpritePath?: string;
|
|
721
|
-
}
|
|
722
|
-
declare const registerWidget: <T extends object>(props: IRegisterWidgetProps<T>) => CustomElementConstructor;
|
|
723
|
-
|
|
724
|
-
declare const importWidgets: (jsFiles: string[]) => Promise<void>;
|
|
725
|
-
|
|
726
600
|
type TCloneable = object | number | string | boolean | symbol | bigint | null | undefined;
|
|
727
601
|
declare const deepClone: <T extends TCloneable>(obj: T) => T;
|
|
728
602
|
|
|
@@ -814,4 +688,4 @@ declare const calculateRenderedTextWidth: (text: string, fontSize: number, isUpp
|
|
|
814
688
|
*/
|
|
815
689
|
declare const isEqual: <T, K>(obj: T | T[], objToCompare: K | K[]) => boolean;
|
|
816
690
|
|
|
817
|
-
export { type IEventBus, type TBooleanish, type TCloneable, type TDict, type TObjType,
|
|
691
|
+
export { type IEventBus, type TBooleanish, type TCloneable, type TDict, type TObjType, busDispatch, busSubscribe, calculateRenderedTextWidth, camelCase, capitalize, capitalizeFirstLetter, checkVisibility, deepClone, deepMerge, isBoolean, isBrowser, isEqual, isFunction, isNull, isNumber, isObject, isString, isUndefined, kebabCase, merge, omit, pascalCase, pick, setStyleProperties, sleep, slugify, storageCalculateSize, storageClear, storageClearByPrefixOrSuffix, storageExists, storageGetAllKeys, storageGetItem, storageRemoveItem, storageSetItem, stringifyBigIntValues, uncapitalize, useEventBus };
|
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pivanov/utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "A collection of personal utilities to live a happier life",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"packageManager": "pnpm@9.0.0",
|
|
7
6
|
"repository": {
|
|
8
7
|
"type": "git",
|
|
9
8
|
"url": "git://github.com/pivanov/pivanov-utils.git"
|
|
@@ -12,16 +11,6 @@
|
|
|
12
11
|
"bugs": {
|
|
13
12
|
"url": "https://github.com/pivanov/pivanov-utils/issues"
|
|
14
13
|
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "rm -rf dist && pnpm rollup -c",
|
|
17
|
-
"test": "pnpm vitest",
|
|
18
|
-
"test:coverage": "pnpm vitest --coverage",
|
|
19
|
-
"test:ui": "pnpm vitest --ui",
|
|
20
|
-
"lint": "biome lint .",
|
|
21
|
-
"format": "biome format . --write",
|
|
22
|
-
"check": "biome check . --write",
|
|
23
|
-
"prepublishOnly": "pnpm build"
|
|
24
|
-
},
|
|
25
14
|
"author": {
|
|
26
15
|
"name": "Pavel Ivanov",
|
|
27
16
|
"email": "iweb.ivanov@gmail.com",
|
|
@@ -31,15 +20,13 @@
|
|
|
31
20
|
"utils",
|
|
32
21
|
"typescript",
|
|
33
22
|
"react",
|
|
34
|
-
"web-components",
|
|
35
23
|
"dom",
|
|
36
24
|
"cache",
|
|
25
|
+
"browser cache api",
|
|
37
26
|
"event-bus",
|
|
38
27
|
"string",
|
|
39
28
|
"object",
|
|
40
|
-
"promise"
|
|
41
|
-
"r2wc",
|
|
42
|
-
"react-to-web-component"
|
|
29
|
+
"promise"
|
|
43
30
|
],
|
|
44
31
|
"license": "MIT",
|
|
45
32
|
"main": "dist/cjs/index.js",
|
|
@@ -52,7 +39,9 @@
|
|
|
52
39
|
"require": "./dist/cjs/index.js"
|
|
53
40
|
}
|
|
54
41
|
},
|
|
55
|
-
"files": [
|
|
42
|
+
"files": [
|
|
43
|
+
"dist/"
|
|
44
|
+
],
|
|
56
45
|
"peerDependencies": {
|
|
57
46
|
"react": ">=18",
|
|
58
47
|
"react-dom": ">=18"
|
|
@@ -85,5 +74,14 @@
|
|
|
85
74
|
},
|
|
86
75
|
"publishConfig": {
|
|
87
76
|
"access": "public"
|
|
77
|
+
},
|
|
78
|
+
"scripts": {
|
|
79
|
+
"build": "rm -rf dist && pnpm rollup -c",
|
|
80
|
+
"test": "pnpm vitest",
|
|
81
|
+
"test:coverage": "pnpm vitest --coverage",
|
|
82
|
+
"test:ui": "pnpm vitest --ui",
|
|
83
|
+
"lint": "biome lint .",
|
|
84
|
+
"format": "biome format . --write",
|
|
85
|
+
"check": "biome check . --write"
|
|
88
86
|
}
|
|
89
|
-
}
|
|
87
|
+
}
|