@sohanemon/utils 4.1.2 → 4.1.4
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 +272 -0
- package/dist/functions/index.d.ts +11 -0
- package/dist/functions/index.js +33 -0
- package/dist/functions/object.d.ts +67 -0
- package/dist/functions/object.js +39 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# sohanemon/utils
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Description
|
|
8
|
+
|
|
9
|
+
`sohanemon/utils` is a collection of utility functions and hooks designed to simplify common tasks in modern web development. It includes utilities for handling objects, cookies, class name merging, and more. The library is built with TypeScript and is fully typed, ensuring a smooth and error-free development experience.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Object Utilities**: Functions to manipulate and access nested object properties.
|
|
14
|
+
- **Cookie Management**: Functions to set, get, delete, and check for cookies.
|
|
15
|
+
- **Class Name Merging**: A utility to merge class names with Tailwind CSS and custom logic.
|
|
16
|
+
- **Responsive Media Queries**: Hooks to detect media queries based on Tailwind CSS breakpoints.
|
|
17
|
+
- **Debounce and Throttle**: Utilities to control the rate of function execution.
|
|
18
|
+
- **Copy to Clipboard**: A hook to copy text to the clipboard and track the copy status.
|
|
19
|
+
- **Local Storage Management**: A hook to persist state in local storage.
|
|
20
|
+
- **URL Parameter Management**: A hook to manage URL parameters as state.
|
|
21
|
+
- **DOM Calculation**: Hooks to calculate dimensions of elements based on viewport and other block dimensions.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
You can install `sohanemon/utils` using npm or yarn:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @sohanemon/utils
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
or
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
yarn add @sohanemon/utils
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Importing Utilities
|
|
40
|
+
|
|
41
|
+
You can import individual utilities or hooks as needed:
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
import { cn, getObjectValue, setClientSideCookie } from '@sohanemon/utils';
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Examples
|
|
48
|
+
|
|
49
|
+
#### Class Name Merging
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
import { cn } from '@sohanemon/utils';
|
|
53
|
+
|
|
54
|
+
const className = cn('bg-blue-500', 'text-white', 'p-4', 'rounded-lg');
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Object Utilities
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
import { getObjectValue } from '@sohanemon/utils';
|
|
61
|
+
|
|
62
|
+
const obj = { a: { b: { c: 1 } } };
|
|
63
|
+
const value = getObjectValue(obj, 'a.b.c'); // 1
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### Cookie Management
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
import { setClientSideCookie, getClientSideCookie } from '@sohanemon/utils';
|
|
70
|
+
|
|
71
|
+
setClientSideCookie('username', 'sohanemon', 7);
|
|
72
|
+
const { value } = getClientSideCookie('username'); // 'sohanemon'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### Responsive Media Queries
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
import { useMediaQuery } from '@sohanemon/utils';
|
|
79
|
+
|
|
80
|
+
const isMobile = useMediaQuery('sm');
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Debounce and Throttle
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
import { debounce, throttle } from '@sohanemon/utils';
|
|
87
|
+
|
|
88
|
+
const debouncedFunction = debounce(() => console.log('Debounced!'), 300);
|
|
89
|
+
const throttledFunction = throttle(() => console.log('Throttled!'), 300);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### Copy to Clipboard
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
import { useCopyToClipboard } from '@sohanemon/utils/hooks';
|
|
96
|
+
|
|
97
|
+
const { isCopied, copy } = useCopyToClipboard();
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div>
|
|
101
|
+
<button onClick={() => copy('Hello, World!')}>Copy</button>
|
|
102
|
+
{isCopied && <span>Copied!</span>}
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### Local Storage Management
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
import { useLocalStorage } from '@sohanemon/utils/hooks';
|
|
111
|
+
|
|
112
|
+
const [value, setValue] = useLocalStorage('myKey', 'initialValue');
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<div>
|
|
116
|
+
<input
|
|
117
|
+
type="text"
|
|
118
|
+
value={value}
|
|
119
|
+
onChange={(e) => setValue(e.target.value)}
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### URL Parameter Management
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
import { useUrlParams } from '@sohanemon/utils/hooks';
|
|
129
|
+
|
|
130
|
+
const [param, setParam] = useUrlParams('myParam', 'defaultValue');
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<div>
|
|
134
|
+
<input
|
|
135
|
+
type="text"
|
|
136
|
+
value={param}
|
|
137
|
+
onChange={(e) => setParam(e.target.value)}
|
|
138
|
+
/>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### DOM Calculation
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
import { useDomCalculation } from '@sohanemon/utils/hooks';
|
|
147
|
+
|
|
148
|
+
const { height, width } = useDomCalculation({
|
|
149
|
+
blockIds: ['header', 'footer'],
|
|
150
|
+
margin: 20,
|
|
151
|
+
substract: true,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<div style={{ height, width }}>
|
|
156
|
+
Content
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## API Documentation
|
|
162
|
+
|
|
163
|
+
### Class Name Merging
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
cn(...inputs: ClassValue[]): string
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Object Utilities
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
getObjectValue<T, K extends Array<string | number>, D>(
|
|
173
|
+
obj: T,
|
|
174
|
+
path: K,
|
|
175
|
+
defaultValue: D
|
|
176
|
+
): Exclude<GetValue<T, K>, undefined> | D;
|
|
177
|
+
|
|
178
|
+
getObjectValue<T, K extends Array<string | number>>(
|
|
179
|
+
obj: T,
|
|
180
|
+
path: K
|
|
181
|
+
): GetValue<T, K> | undefined;
|
|
182
|
+
|
|
183
|
+
getObjectValue<T, S extends string, D>(
|
|
184
|
+
obj: T,
|
|
185
|
+
path: S,
|
|
186
|
+
defaultValue: D
|
|
187
|
+
): Exclude<GetValue<T, SplitPath<S>>, undefined> | D;
|
|
188
|
+
|
|
189
|
+
getObjectValue<T, S extends string>(
|
|
190
|
+
obj: T,
|
|
191
|
+
path: S
|
|
192
|
+
): GetValue<T, SplitPath<S>> | undefined;
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Cookie Management
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
setClientSideCookie(name: string, value: string, days?: number, path?: string): void
|
|
199
|
+
deleteClientSideCookie(name: string, path?: string): void
|
|
200
|
+
hasClientSideCookie(name: string): boolean
|
|
201
|
+
getClientSideCookie(name: string): { value: string | undefined }
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Responsive Media Queries
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
useMediaQuery(tailwindBreakpoint: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | `(${string})`): boolean
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Debounce and Throttle
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
debounce<F extends (...args: any[]) => any>(
|
|
214
|
+
function_: F,
|
|
215
|
+
wait?: number,
|
|
216
|
+
options?: { immediate?: boolean }
|
|
217
|
+
): DebouncedFunction<F>
|
|
218
|
+
|
|
219
|
+
throttle<F extends (...args: any[]) => any>(
|
|
220
|
+
function_: F,
|
|
221
|
+
wait?: number,
|
|
222
|
+
options?: { leading?: boolean; trailing?: boolean }
|
|
223
|
+
): ThrottledFunction<F>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Copy to Clipboard
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
useCopyToClipboard({ timeout?: number }): { isCopied: boolean; copy: (value: string) => void }
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Local Storage Management
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
useLocalStorage<T extends Record<string, any>>(key: string, defaultValue: T): LocalStorageValue<T>
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### URL Parameter Management
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
useUrlParams<T extends string | number | boolean>(key: string, defaultValue: T): [T, (value: T) => void]
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### DOM Calculation
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
useDomCalculation({
|
|
248
|
+
blockIds: string[];
|
|
249
|
+
dynamic?: boolean | string;
|
|
250
|
+
margin?: number;
|
|
251
|
+
substract?: boolean;
|
|
252
|
+
onChange?: (results: {
|
|
253
|
+
blocksHeight: number;
|
|
254
|
+
blocksWidth: number;
|
|
255
|
+
remainingWidth: number;
|
|
256
|
+
remainingHeight: number;
|
|
257
|
+
}) => void;
|
|
258
|
+
}): { height: number; width: number }
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Contributing
|
|
262
|
+
|
|
263
|
+
Contributions are welcome! Please read the [CONTRIBUTING.md](CONTRIBUTING.md) file for guidelines on how to contribute to the project.
|
|
264
|
+
|
|
265
|
+
## License
|
|
266
|
+
|
|
267
|
+
This project is licensed under the ISC License - see the [LICENSE](LICENSE) file for details.
|
|
268
|
+
|
|
269
|
+
## Contact
|
|
270
|
+
|
|
271
|
+
- **Sohan Emon**: [sohanemon@outlook.com](mailto:sohanemon@outlook.com)
|
|
272
|
+
- **GitHub**: [sohanemon](https://github.com/sohanemon)
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { type ClassValue } from 'clsx';
|
|
8
8
|
import type * as React from 'react';
|
|
9
9
|
export * from './cookie';
|
|
10
|
+
export * from './object';
|
|
10
11
|
export declare function cn(...inputs: ClassValue[]): string;
|
|
11
12
|
/**
|
|
12
13
|
* @deprecated Use isLinkActive instead.
|
|
@@ -60,6 +61,16 @@ export declare const copyToClipboard: (value: string, onSuccess?: () => void) =>
|
|
|
60
61
|
* @returns - Normal Case
|
|
61
62
|
*/
|
|
62
63
|
export declare function convertToNormalCase(inputString: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* Converts a string to a URL-friendly slug by trimming, converting to lowercase,
|
|
66
|
+
* replacing diacritics, removing invalid characters, and replacing spaces with hyphens.
|
|
67
|
+
* @param {string} [str] - The input string to convert.
|
|
68
|
+
* @returns {string} The generated slug.
|
|
69
|
+
* @example
|
|
70
|
+
* convertToSlug("Hello World!"); // "hello-world"
|
|
71
|
+
* convertToSlug("Déjà Vu"); // "deja-vu"
|
|
72
|
+
*/
|
|
73
|
+
export declare const convertToSlug: (str: string) => string;
|
|
63
74
|
/**
|
|
64
75
|
* Checks if the code is running in a server-side environment.
|
|
65
76
|
*
|
package/dist/functions/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { clsx } from 'clsx';
|
|
|
8
8
|
import { extendTailwindMerge } from 'tailwind-merge';
|
|
9
9
|
import { withFluid } from '@fluid-tailwind/tailwind-merge';
|
|
10
10
|
export * from './cookie';
|
|
11
|
+
export * from './object';
|
|
11
12
|
export function cn(...inputs) {
|
|
12
13
|
const twMerge = extendTailwindMerge(withFluid);
|
|
13
14
|
return twMerge(clsx(inputs));
|
|
@@ -109,6 +110,38 @@ export function convertToNormalCase(inputString) {
|
|
|
109
110
|
const capitalizedWords = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1));
|
|
110
111
|
return capitalizedWords.join(' ');
|
|
111
112
|
}
|
|
113
|
+
const from = 'àáãäâèéëêìíïîòóöôùúüûñç·/_,:;';
|
|
114
|
+
const to = 'aaaaaeeeeiiiioooouuuunc------';
|
|
115
|
+
/**
|
|
116
|
+
* Converts a string to a URL-friendly slug by trimming, converting to lowercase,
|
|
117
|
+
* replacing diacritics, removing invalid characters, and replacing spaces with hyphens.
|
|
118
|
+
* @param {string} [str] - The input string to convert.
|
|
119
|
+
* @returns {string} The generated slug.
|
|
120
|
+
* @example
|
|
121
|
+
* convertToSlug("Hello World!"); // "hello-world"
|
|
122
|
+
* convertToSlug("Déjà Vu"); // "deja-vu"
|
|
123
|
+
*/
|
|
124
|
+
export const convertToSlug = (str) => {
|
|
125
|
+
if (typeof str !== 'string') {
|
|
126
|
+
throw new TypeError('Input must be a string');
|
|
127
|
+
}
|
|
128
|
+
// Trim the string and convert it to lowercase.
|
|
129
|
+
let slug = str.trim().toLowerCase();
|
|
130
|
+
// Build a mapping of accented characters to their non-accented equivalents.
|
|
131
|
+
const charMap = {};
|
|
132
|
+
for (let i = 0; i < from.length; i++) {
|
|
133
|
+
charMap[from.charAt(i)] = to.charAt(i);
|
|
134
|
+
}
|
|
135
|
+
// Replace all accented characters using the mapping.
|
|
136
|
+
slug = slug.replace(new RegExp(`[${from}]`, 'g'), (match) => charMap[match] || match);
|
|
137
|
+
return (slug
|
|
138
|
+
.replace(/[^a-z0-9 -]/g, '') // Remove invalid characters
|
|
139
|
+
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
|
140
|
+
.replace(/-+/g, '-') // Collapse consecutive hyphens
|
|
141
|
+
.replace(/^-+/, '') // Remove leading hyphens
|
|
142
|
+
.replace(/-+$/, '') || // Remove trailing hyphens
|
|
143
|
+
'');
|
|
144
|
+
};
|
|
112
145
|
/**
|
|
113
146
|
* Checks if the code is running in a server-side environment.
|
|
114
147
|
*
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type representing a path split into segments
|
|
3
|
+
* @template S - The original path string type
|
|
4
|
+
*/
|
|
5
|
+
type SplitPath<S extends string> = S extends `${infer First}.${infer Rest}` ? [First, ...SplitPath<Rest>] : [S];
|
|
6
|
+
/**
|
|
7
|
+
* Recursive type to resolve nested object types based on path
|
|
8
|
+
* @template T - Current object type
|
|
9
|
+
* @template K - Array of path segments
|
|
10
|
+
*/
|
|
11
|
+
type GetValue<T, K extends Array<string | number>> = K extends [
|
|
12
|
+
infer First,
|
|
13
|
+
...infer Rest
|
|
14
|
+
] ? First extends keyof T ? GetValue<T[First], Rest extends Array<string | number> ? Rest : []> : First extends `${number}` ? T extends any[] ? GetValue<T[number], Rest extends Array<string | number> ? Rest : []> : undefined : undefined : T;
|
|
15
|
+
/**
|
|
16
|
+
* Get a nested value from an object using array path segments
|
|
17
|
+
* @template T - Object type
|
|
18
|
+
* @template K - Path segments array type
|
|
19
|
+
* @template D - Default value type
|
|
20
|
+
* @param obj - Source object
|
|
21
|
+
* @param path - Array of path segments
|
|
22
|
+
* @param defaultValue - Fallback value if path not found
|
|
23
|
+
* @returns Value at path or default value
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* getObjectValue({a: [{b: 1}]}, ['a', 0, 'b']) // 1
|
|
27
|
+
*/
|
|
28
|
+
export declare function getObjectValue<T, K extends Array<string | number>, D>(obj: T, path: K, defaultValue: D): Exclude<GetValue<T, K>, undefined> | D;
|
|
29
|
+
/**
|
|
30
|
+
* Get a nested value from an object using array path segments
|
|
31
|
+
* @template T - Object type
|
|
32
|
+
* @template K - Path segments array type
|
|
33
|
+
* @param obj - Source object
|
|
34
|
+
* @param path - Array of path segments
|
|
35
|
+
* @returns Value at path or undefined
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* getObjectValue({a: [{b: 1}]}, ['a', 0, 'b']) // 1
|
|
39
|
+
*/
|
|
40
|
+
export declare function getObjectValue<T, K extends Array<string | number>>(obj: T, path: K): GetValue<T, K> | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Get a nested value from an object using dot notation path
|
|
43
|
+
* @template T - Object type
|
|
44
|
+
* @template S - Path string literal type
|
|
45
|
+
* @template D - Default value type
|
|
46
|
+
* @param obj - Source object
|
|
47
|
+
* @param path - Dot-separated path string
|
|
48
|
+
* @param defaultValue - Fallback value if path not found
|
|
49
|
+
* @returns Value at path or default value
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* getObjectValue({a: [{b: 1}]}, 'a.0.b', 2) // 1
|
|
53
|
+
*/
|
|
54
|
+
export declare function getObjectValue<T, S extends string, D>(obj: T, path: S, defaultValue: D): Exclude<GetValue<T, SplitPath<S>>, undefined> | D;
|
|
55
|
+
/**
|
|
56
|
+
* Get a nested value from an object using dot notation path
|
|
57
|
+
* @template T - Object type
|
|
58
|
+
* @template S - Path string literal type
|
|
59
|
+
* @param obj - Source object
|
|
60
|
+
* @param path - Dot-separated path string
|
|
61
|
+
* @returns Value at path or undefined
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* getObjectValue({a: [{b: 1}]}, 'a.0.b') // 1
|
|
65
|
+
*/
|
|
66
|
+
export declare function getObjectValue<T, S extends string>(obj: T, path: S): GetValue<T, SplitPath<S>> | undefined;
|
|
67
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implementation of deep object value retrieval with type safety
|
|
3
|
+
* @param obj - Source object
|
|
4
|
+
* @param path - Path specification (string or array)
|
|
5
|
+
* @param defaultValue - Optional fallback value
|
|
6
|
+
* @returns Value at path or default/undefined
|
|
7
|
+
*/
|
|
8
|
+
export function getObjectValue(obj, path, defaultValue) {
|
|
9
|
+
// Validate path type and handle edge cases
|
|
10
|
+
if (typeof path !== 'string' && !Array.isArray(path)) {
|
|
11
|
+
return defaultValue;
|
|
12
|
+
}
|
|
13
|
+
// Ensure pathArray is always an array
|
|
14
|
+
const pathArray = (() => {
|
|
15
|
+
if (Array.isArray(path))
|
|
16
|
+
return path;
|
|
17
|
+
if (path === '')
|
|
18
|
+
return [];
|
|
19
|
+
return String(path)
|
|
20
|
+
.split('.')
|
|
21
|
+
.filter((segment) => segment !== '');
|
|
22
|
+
})();
|
|
23
|
+
// Final safety check for array type
|
|
24
|
+
if (!Array.isArray(pathArray)) {
|
|
25
|
+
return defaultValue;
|
|
26
|
+
}
|
|
27
|
+
let current = obj;
|
|
28
|
+
for (const key of pathArray) {
|
|
29
|
+
if (current === null || current === undefined) {
|
|
30
|
+
return defaultValue;
|
|
31
|
+
}
|
|
32
|
+
// Convert numeric strings to numbers for arrays
|
|
33
|
+
const actualKey = typeof key === 'string' && Array.isArray(current) && /^\d+$/.test(key)
|
|
34
|
+
? Number.parseInt(key, 10)
|
|
35
|
+
: key;
|
|
36
|
+
current = current[actualKey];
|
|
37
|
+
}
|
|
38
|
+
return current !== undefined ? current : defaultValue;
|
|
39
|
+
}
|