@sohanemon/utils 6.2.8 → 6.3.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/dist/components.d.ts +84 -0
- package/dist/components.js +1 -0
- package/dist/hooks-hkNH7WgA.js +1 -0
- package/dist/hooks.d.ts +309 -0
- package/dist/hooks.js +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +665 -0
- package/dist/index.d.ts +665 -2
- package/dist/index.js +1 -2
- package/package.json +49 -32
- package/dist/components/html-injector.d.ts +0 -50
- package/dist/components/html-injector.js +0 -108
- package/dist/components/index.d.ts +0 -5
- package/dist/components/index.js +0 -7
- package/dist/components/media-wrapper.d.ts +0 -10
- package/dist/components/media-wrapper.js +0 -14
- package/dist/components/responsive-indicator.d.ts +0 -2
- package/dist/components/responsive-indicator.js +0 -68
- package/dist/components/scrollable-marker.d.ts +0 -1
- package/dist/components/scrollable-marker.js +0 -56
- package/dist/functions/cookie.d.ts +0 -6
- package/dist/functions/cookie.js +0 -22
- package/dist/functions/deepmerge.d.ts +0 -59
- package/dist/functions/deepmerge.js +0 -116
- package/dist/functions/hydrate.d.ts +0 -15
- package/dist/functions/hydrate.js +0 -31
- package/dist/functions/index.d.ts +0 -8
- package/dist/functions/index.js +0 -8
- package/dist/functions/object.d.ts +0 -93
- package/dist/functions/object.js +0 -67
- package/dist/functions/poll.d.ts +0 -38
- package/dist/functions/poll.js +0 -69
- package/dist/functions/schedule.d.ts +0 -12
- package/dist/functions/schedule.js +0 -29
- package/dist/functions/shield.d.ts +0 -18
- package/dist/functions/shield.js +0 -15
- package/dist/functions/utils.d.ts +0 -243
- package/dist/functions/utils.js +0 -439
- package/dist/hooks/action.d.ts +0 -20
- package/dist/hooks/action.js +0 -84
- package/dist/hooks/async.d.ts +0 -30
- package/dist/hooks/async.js +0 -82
- package/dist/hooks/index.d.ts +0 -192
- package/dist/hooks/index.js +0 -533
- package/dist/hooks/schedule.d.ts +0 -36
- package/dist/hooks/schedule.js +0 -68
- package/dist/types/gates.d.ts +0 -133
- package/dist/types/gates.js +0 -1
- package/dist/types/guards.d.ts +0 -6
- package/dist/types/guards.js +0 -29
- package/dist/types/index.d.ts +0 -3
- package/dist/types/index.js +0 -3
- package/dist/types/utilities.d.ts +0 -62
- package/dist/types/utilities.js +0 -1
package/dist/functions/utils.js
DELETED
|
@@ -1,439 +0,0 @@
|
|
|
1
|
-
import { clsx } from 'clsx';
|
|
2
|
-
import { twMerge } from 'tailwind-merge';
|
|
3
|
-
/**
|
|
4
|
-
* Utility to merge class names with Tailwind CSS and additional custom merging logic.
|
|
5
|
-
*
|
|
6
|
-
* @param {...ClassValue[]} inputs - Class names to merge.
|
|
7
|
-
* @returns {string} - A string of merged class names.
|
|
8
|
-
*/
|
|
9
|
-
export function cn(...inputs) {
|
|
10
|
-
return twMerge(clsx(inputs));
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* @deprecated Use isLinkActive instead.
|
|
14
|
-
*
|
|
15
|
-
* Determines if a navigation link is active based on the current path.
|
|
16
|
-
*
|
|
17
|
-
* @param href - The target URL.
|
|
18
|
-
* @param path - The current browser path.
|
|
19
|
-
* @returns - True if the navigation is active, false otherwise.
|
|
20
|
-
*/
|
|
21
|
-
export function isNavActive(href, path) {
|
|
22
|
-
console.warn('isNavActive is deprecated. Use isLinkActive instead.');
|
|
23
|
-
const regex = new RegExp(`^/?${href}(/|$)`);
|
|
24
|
-
return regex.test(path);
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Checks if a link is active, considering optional localization prefixes.
|
|
28
|
-
*
|
|
29
|
-
* @param {Object} params - Parameters object.
|
|
30
|
-
* @param {string} params.path - The target path of the link.
|
|
31
|
-
* @param {string} params.currentPath - The current browser path.
|
|
32
|
-
* @param {string[]} [params.locales=['en', 'es', 'de', 'zh', 'bn', 'fr', 'it', 'nl']] - Supported locale prefixes.
|
|
33
|
-
* @returns {boolean} - True if the link is active, false otherwise.
|
|
34
|
-
*/
|
|
35
|
-
export function isLinkActive({ path, currentPath, locales = ['en', 'es', 'de', 'zh', 'bn', 'fr', 'it', 'nl'], exact = true, }) {
|
|
36
|
-
const localeRegex = new RegExp(`^/?(${locales.join('|')})/`);
|
|
37
|
-
const normalizePath = (p) => {
|
|
38
|
-
return p
|
|
39
|
-
.replace(localeRegex, '') // Remove localization prefix (e.g., en/, fr/, etc.)
|
|
40
|
-
.replace(/^\/+|\/+$/g, ''); // Trim leading and trailing slashes
|
|
41
|
-
};
|
|
42
|
-
const normalizedPath = normalizePath(path);
|
|
43
|
-
const normalizedCurrentPath = normalizePath(currentPath);
|
|
44
|
-
return exact
|
|
45
|
-
? normalizedPath === normalizedCurrentPath
|
|
46
|
-
: normalizedCurrentPath.startsWith(normalizedPath);
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Cleans a file path by removing the `/public/` prefix if present.
|
|
50
|
-
*
|
|
51
|
-
* @param src - The source path to clean.
|
|
52
|
-
* @returns - The cleaned path.
|
|
53
|
-
*/
|
|
54
|
-
export function cleanSrc(src) {
|
|
55
|
-
let cleanedSrc = src;
|
|
56
|
-
if (src.includes('/public/')) {
|
|
57
|
-
cleanedSrc = src.replace('/public/', '/');
|
|
58
|
-
}
|
|
59
|
-
return cleanedSrc.trim();
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Smoothly scrolls to the top or bottom of a specified container.
|
|
63
|
-
*
|
|
64
|
-
* @param containerSelector - The CSS selector or React ref for the container.
|
|
65
|
-
* @param to - Specifies whether to scroll to the top or bottom.
|
|
66
|
-
*/
|
|
67
|
-
export const scrollTo = (containerSelector, to) => {
|
|
68
|
-
let container;
|
|
69
|
-
if (typeof containerSelector === 'string') {
|
|
70
|
-
container = document.querySelector(containerSelector);
|
|
71
|
-
}
|
|
72
|
-
else if (containerSelector.current) {
|
|
73
|
-
container = containerSelector.current;
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
if (container) {
|
|
79
|
-
container.scrollTo({
|
|
80
|
-
top: to === 'top' ? 0 : container.scrollHeight - container.clientHeight,
|
|
81
|
-
behavior: 'smooth',
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
/**
|
|
86
|
-
* Copies a given string to the clipboard.
|
|
87
|
-
*
|
|
88
|
-
* @param value - The value to copy to the clipboard.
|
|
89
|
-
* @param [onSuccess=() => {}] - Optional callback executed after successful copy.
|
|
90
|
-
*/
|
|
91
|
-
export const copyToClipboard = (value, onSuccess = () => { }) => {
|
|
92
|
-
if (typeof window === 'undefined' || !navigator.clipboard?.writeText) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
if (!value) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
navigator.clipboard.writeText(value).then(onSuccess);
|
|
99
|
-
};
|
|
100
|
-
/**
|
|
101
|
-
* Converts camelCase, PascalCase, kebab-case, snake_case into normal case.
|
|
102
|
-
*
|
|
103
|
-
* @param inputString - The string need to be converted into normal case
|
|
104
|
-
* @returns - Normal Case
|
|
105
|
-
*/
|
|
106
|
-
export function convertToNormalCase(inputString) {
|
|
107
|
-
const splittedString = inputString.split('.').pop();
|
|
108
|
-
const string = splittedString || inputString;
|
|
109
|
-
const words = string.replace(/([a-z])([A-Z])/g, '$1 $2').split(/[-_|�\s]+/);
|
|
110
|
-
const capitalizedWords = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1));
|
|
111
|
-
return capitalizedWords.join(' ');
|
|
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
|
-
};
|
|
145
|
-
/**
|
|
146
|
-
* Checks if the code is running in a server-side environment.
|
|
147
|
-
*
|
|
148
|
-
* @returns - True if the code is executed in SSR (Server-Side Rendering) context, false otherwise
|
|
149
|
-
*/
|
|
150
|
-
export const isSSR = typeof window === 'undefined';
|
|
151
|
-
/**
|
|
152
|
-
* Converts an SVG string to a Base64-encoded string.
|
|
153
|
-
*
|
|
154
|
-
* @param str - The SVG string to encode
|
|
155
|
-
* @returns - Base64-encoded string representation of the SVG
|
|
156
|
-
*/
|
|
157
|
-
export const svgToBase64 = (str) => isSSR ? Buffer.from(str).toString('base64') : window.btoa(str);
|
|
158
|
-
/**
|
|
159
|
-
* Pauses execution for the specified time.
|
|
160
|
-
*
|
|
161
|
-
* `signal` allows cancelling the sleep via AbortSignal.
|
|
162
|
-
*
|
|
163
|
-
* @param time - Time in milliseconds to sleep (default is 1000ms)
|
|
164
|
-
* @param signal - Optional AbortSignal to cancel the sleep early
|
|
165
|
-
* @returns - A Promise that resolves after the specified time or when aborted
|
|
166
|
-
*/
|
|
167
|
-
export const sleep = (time = 1000, signal) => new Promise((resolve) => {
|
|
168
|
-
if (signal?.aborted)
|
|
169
|
-
return resolve();
|
|
170
|
-
const id = setTimeout(() => {
|
|
171
|
-
cleanup();
|
|
172
|
-
resolve();
|
|
173
|
-
}, time);
|
|
174
|
-
function onAbort() {
|
|
175
|
-
clearTimeout(id);
|
|
176
|
-
cleanup();
|
|
177
|
-
resolve();
|
|
178
|
-
}
|
|
179
|
-
function cleanup() {
|
|
180
|
-
signal?.removeEventListener('abort', onAbort);
|
|
181
|
-
}
|
|
182
|
-
// only add listener if a signal was supplied
|
|
183
|
-
if (signal) {
|
|
184
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
/**
|
|
188
|
-
* Creates a debounced function that delays invoking the provided function until
|
|
189
|
-
* after the specified `wait` time has elapsed since the last invocation.
|
|
190
|
-
*
|
|
191
|
-
* If the `immediate` option is set to `true`, the function will be invoked immediately
|
|
192
|
-
* on the leading edge of the wait interval. Subsequent calls during the wait interval
|
|
193
|
-
* will reset the timer but not invoke the function until the interval elapses again.
|
|
194
|
-
*
|
|
195
|
-
* The returned function includes the `isPending` property to check if the debounce
|
|
196
|
-
* timer is currently active.
|
|
197
|
-
*
|
|
198
|
-
* @typeParam F - The type of the function to debounce.
|
|
199
|
-
*
|
|
200
|
-
* @param function_ - The function to debounce.
|
|
201
|
-
* @param wait - The number of milliseconds to delay (default is 100ms).
|
|
202
|
-
* @param options - An optional object with the following properties:
|
|
203
|
-
* - `immediate` (boolean): If `true`, invokes the function on the leading edge
|
|
204
|
-
* of the wait interval instead of the trailing edge.
|
|
205
|
-
*
|
|
206
|
-
* @returns A debounced version of the provided function, enhanced with the `isPending` property.
|
|
207
|
-
*
|
|
208
|
-
* @throws {TypeError} If the first parameter is not a function.
|
|
209
|
-
* @throws {RangeError} If the `wait` parameter is negative.
|
|
210
|
-
*
|
|
211
|
-
* @example
|
|
212
|
-
* const log = debounce((message: string) => console.log(message), 200);
|
|
213
|
-
* log('Hello'); // Logs "Hello" after 200ms if no other call is made.
|
|
214
|
-
* console.log(log.isPending); // true if the timer is active.
|
|
215
|
-
*/
|
|
216
|
-
export function debounce(function_, wait = 100, options) {
|
|
217
|
-
if (typeof function_ !== 'function') {
|
|
218
|
-
throw new TypeError(`Expected the first parameter to be a function, got \`${typeof function_}\`.`);
|
|
219
|
-
}
|
|
220
|
-
if (wait < 0) {
|
|
221
|
-
throw new RangeError('`wait` must not be negative.');
|
|
222
|
-
}
|
|
223
|
-
const immediate = options?.immediate ?? false;
|
|
224
|
-
let timeoutId;
|
|
225
|
-
let lastArgs;
|
|
226
|
-
let lastContext;
|
|
227
|
-
let result;
|
|
228
|
-
function run() {
|
|
229
|
-
result = function_.apply(lastContext, lastArgs);
|
|
230
|
-
lastArgs = undefined;
|
|
231
|
-
lastContext = undefined;
|
|
232
|
-
return result;
|
|
233
|
-
}
|
|
234
|
-
const debounced = function (...args) {
|
|
235
|
-
lastArgs = args;
|
|
236
|
-
lastContext = this;
|
|
237
|
-
if (timeoutId === undefined && immediate) {
|
|
238
|
-
result = run.call(this);
|
|
239
|
-
}
|
|
240
|
-
if (timeoutId !== undefined) {
|
|
241
|
-
clearTimeout(timeoutId);
|
|
242
|
-
}
|
|
243
|
-
timeoutId = setTimeout(run.bind(this), wait);
|
|
244
|
-
return result;
|
|
245
|
-
};
|
|
246
|
-
Object.defineProperty(debounced, 'isPending', {
|
|
247
|
-
get() {
|
|
248
|
-
return timeoutId !== undefined;
|
|
249
|
-
},
|
|
250
|
-
});
|
|
251
|
-
return debounced;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Creates a throttled function that invokes the provided function at most once
|
|
255
|
-
* every `wait` milliseconds.
|
|
256
|
-
*
|
|
257
|
-
* If the `leading` option is set to `true`, the function will be invoked immediately
|
|
258
|
-
* on the leading edge of the throttle interval. If the `trailing` option is set to `true`,
|
|
259
|
-
* the function will also be invoked at the end of the throttle interval if additional
|
|
260
|
-
* calls were made during the interval.
|
|
261
|
-
*
|
|
262
|
-
* The returned function includes the `isPending` property to check if the throttle
|
|
263
|
-
* timer is currently active.
|
|
264
|
-
*
|
|
265
|
-
* @typeParam F - The type of the function to throttle.
|
|
266
|
-
*
|
|
267
|
-
* @param function_ - The function to throttle.
|
|
268
|
-
* @param wait - The number of milliseconds to wait between invocations (default is 100ms).
|
|
269
|
-
* @param options - An optional object with the following properties:
|
|
270
|
-
* - `leading` (boolean): If `true`, invokes the function on the leading edge of the interval.
|
|
271
|
-
* - `trailing` (boolean): If `true`, invokes the function on the trailing edge of the interval.
|
|
272
|
-
*
|
|
273
|
-
* @returns A throttled version of the provided function, enhanced with the `isPending` property.
|
|
274
|
-
*
|
|
275
|
-
* @throws {TypeError} If the first parameter is not a function.
|
|
276
|
-
* @throws {RangeError} If the `wait` parameter is negative.
|
|
277
|
-
*
|
|
278
|
-
* @example
|
|
279
|
-
* const log = throttle((message: string) => console.log(message), 200);
|
|
280
|
-
* log('Hello'); // Logs "Hello" immediately if leading is true.
|
|
281
|
-
* console.log(log.isPending); // true if the timer is active.
|
|
282
|
-
*/
|
|
283
|
-
export function throttle(function_, wait = 100, options) {
|
|
284
|
-
if (typeof function_ !== 'function') {
|
|
285
|
-
throw new TypeError(`Expected the first parameter to be a function, got \`${typeof function_}\`.`);
|
|
286
|
-
}
|
|
287
|
-
if (wait < 0) {
|
|
288
|
-
throw new RangeError('`wait` must not be negative.');
|
|
289
|
-
}
|
|
290
|
-
const leading = options?.leading ?? true;
|
|
291
|
-
const trailing = options?.trailing ?? true;
|
|
292
|
-
let timeoutId;
|
|
293
|
-
let lastArgs;
|
|
294
|
-
let lastContext;
|
|
295
|
-
let lastCallTime;
|
|
296
|
-
let result;
|
|
297
|
-
function invoke() {
|
|
298
|
-
lastCallTime = Date.now();
|
|
299
|
-
result = function_.apply(lastContext, lastArgs);
|
|
300
|
-
lastArgs = undefined;
|
|
301
|
-
lastContext = undefined;
|
|
302
|
-
}
|
|
303
|
-
function later() {
|
|
304
|
-
timeoutId = undefined;
|
|
305
|
-
if (trailing && lastArgs) {
|
|
306
|
-
invoke();
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
const throttled = function (...args) {
|
|
310
|
-
const now = Date.now();
|
|
311
|
-
const timeSinceLastCall = lastCallTime
|
|
312
|
-
? now - lastCallTime
|
|
313
|
-
: Number.POSITIVE_INFINITY;
|
|
314
|
-
lastArgs = args;
|
|
315
|
-
lastContext = this;
|
|
316
|
-
if (timeSinceLastCall >= wait) {
|
|
317
|
-
if (leading) {
|
|
318
|
-
invoke();
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
321
|
-
timeoutId = setTimeout(later, wait);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
else if (!timeoutId && trailing) {
|
|
325
|
-
timeoutId = setTimeout(later, wait - timeSinceLastCall);
|
|
326
|
-
}
|
|
327
|
-
return result;
|
|
328
|
-
};
|
|
329
|
-
Object.defineProperty(throttled, 'isPending', {
|
|
330
|
-
get() {
|
|
331
|
-
return timeoutId !== undefined;
|
|
332
|
-
},
|
|
333
|
-
});
|
|
334
|
-
return throttled;
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Formats a string by replacing each '%s' placeholder with the corresponding argument.
|
|
338
|
-
* This function mimics the basic behavior of C's printf for %s substitution.
|
|
339
|
-
*
|
|
340
|
-
* It supports both calls like `printf(format, ...args)` and `printf(format, argsArray)`.
|
|
341
|
-
*
|
|
342
|
-
* @param format - The format string containing '%s' placeholders.
|
|
343
|
-
* @param args - The values to substitute into the placeholders, either as separate arguments or as a single array.
|
|
344
|
-
* @returns The formatted string with all '%s' replaced by the provided arguments.
|
|
345
|
-
*
|
|
346
|
-
* @example
|
|
347
|
-
* ```ts
|
|
348
|
-
* const message = printf("%s love %s", "I", "Bangladesh");
|
|
349
|
-
* // message === "I love Bangladesh"
|
|
350
|
-
*
|
|
351
|
-
* const arr = ["I", "Bangladesh"];
|
|
352
|
-
* const message2 = printf("%s love %s", arr);
|
|
353
|
-
* // message2 === "I love Bangladesh"
|
|
354
|
-
*
|
|
355
|
-
* // If there are too few arguments:
|
|
356
|
-
* const incomplete = printf("Bangladesh is %s %s", "beautiful");
|
|
357
|
-
* // incomplete === "Bangladesh is beautiful"
|
|
358
|
-
* ```
|
|
359
|
-
*/
|
|
360
|
-
export function printf(format, ...args) {
|
|
361
|
-
const replacements = args.length === 1 && Array.isArray(args[0]) ? args[0] : args;
|
|
362
|
-
let idx = 0;
|
|
363
|
-
return format.replace(/%s/g, () => {
|
|
364
|
-
const arg = replacements[idx++];
|
|
365
|
-
return arg === undefined ? '' : String(arg);
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
export const mergeRefs = (...refs) => {
|
|
369
|
-
return (value) => {
|
|
370
|
-
for (const ref of refs) {
|
|
371
|
-
if (!ref)
|
|
372
|
-
continue;
|
|
373
|
-
if (typeof ref === 'function') {
|
|
374
|
-
ref(value);
|
|
375
|
-
}
|
|
376
|
-
else {
|
|
377
|
-
ref.current = value;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
};
|
|
381
|
-
};
|
|
382
|
-
/**
|
|
383
|
-
* Navigates to the specified client-side hash without ssr.
|
|
384
|
-
* use `scroll-margin-top` with css to add margins
|
|
385
|
-
*
|
|
386
|
-
* @param id - The ID of the element without # to navigate to.
|
|
387
|
-
*
|
|
388
|
-
* @example goToClientSideHash('my-element');
|
|
389
|
-
*/
|
|
390
|
-
export function goToClientSideHash(id, opts) {
|
|
391
|
-
const el = document.getElementById(id);
|
|
392
|
-
if (!el)
|
|
393
|
-
return;
|
|
394
|
-
el.scrollIntoView({ behavior: 'smooth', block: 'start', ...opts });
|
|
395
|
-
window.history.pushState(null, '', `#${id}`);
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* Escapes a string for use in a regular expression.
|
|
399
|
-
*
|
|
400
|
-
* @param str - The string to escape
|
|
401
|
-
* @returns - The escaped string
|
|
402
|
-
*
|
|
403
|
-
* @example
|
|
404
|
-
* const escapedString = escapeRegExp('Hello, world!');
|
|
405
|
-
* // escapedString === 'Hello\\, world!'
|
|
406
|
-
*/
|
|
407
|
-
export function escapeRegExp(str) {
|
|
408
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Normalizes a string by:
|
|
412
|
-
* - Applying Unicode normalization (NFC)
|
|
413
|
-
* - Optionally removing diacritic marks (accents)
|
|
414
|
-
* - Optionally trimming leading/trailing non-alphanumeric characters
|
|
415
|
-
* - Optionally converting to lowercase
|
|
416
|
-
*
|
|
417
|
-
* @param str - The string to normalize
|
|
418
|
-
* @param options - Normalization options
|
|
419
|
-
* @param options.lowercase - Whether to convert the result to lowercase (default: true)
|
|
420
|
-
* @param options.removeAccents - Whether to remove diacritic marks like accents (default: true)
|
|
421
|
-
* @param options.removeNonAlphanumeric - Whether to trim non-alphanumeric characters from the edges (default: true)
|
|
422
|
-
* @returns The normalized string
|
|
423
|
-
*/
|
|
424
|
-
export function normalizeText(str, options = {}) {
|
|
425
|
-
if (!str)
|
|
426
|
-
return '';
|
|
427
|
-
const { lowercase = true, removeAccents = true, removeNonAlphanumeric = true, } = options;
|
|
428
|
-
let result = str.normalize('NFC');
|
|
429
|
-
if (removeAccents) {
|
|
430
|
-
result = result.normalize('NFD').replace(/\p{M}/gu, ''); // decompose and remove accents
|
|
431
|
-
}
|
|
432
|
-
if (removeNonAlphanumeric) {
|
|
433
|
-
result = result.replace(/^[^\p{L}\p{N}]*|[^\p{L}\p{N}]*$/gu, ''); // trim edges
|
|
434
|
-
}
|
|
435
|
-
if (lowercase) {
|
|
436
|
-
result = result.toLocaleLowerCase();
|
|
437
|
-
}
|
|
438
|
-
return result;
|
|
439
|
-
}
|
package/dist/hooks/action.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
type ActionType<Input, Result> = (input: Input) => Promise<Result>;
|
|
2
|
-
interface UseActionOptions<_Input, Result> {
|
|
3
|
-
onSuccess?: (data: Result) => void;
|
|
4
|
-
onError?: (error: Error) => void;
|
|
5
|
-
onSettled?: () => void;
|
|
6
|
-
}
|
|
7
|
-
export declare const useAction: <Input, Result>(action: ActionType<Input, Result>, options?: UseActionOptions<Input, Result>) => {
|
|
8
|
-
execute: (input: Input) => void;
|
|
9
|
-
executeAsync: (input: Input) => Promise<Result>;
|
|
10
|
-
reset: () => void;
|
|
11
|
-
useExecute: (input: Input) => void;
|
|
12
|
-
data: Result;
|
|
13
|
-
error: Error;
|
|
14
|
-
input: Input;
|
|
15
|
-
isIdle: boolean;
|
|
16
|
-
isLoading: boolean;
|
|
17
|
-
isSuccess: boolean;
|
|
18
|
-
isError: boolean;
|
|
19
|
-
};
|
|
20
|
-
export {};
|
package/dist/hooks/action.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { useIsomorphicEffect } from '.';
|
|
3
|
-
export const useAction = (action, options) => {
|
|
4
|
-
const [status, setStatus] = React.useState('idle');
|
|
5
|
-
const [data, setData] = React.useState(null);
|
|
6
|
-
const [error, setError] = React.useState(null);
|
|
7
|
-
const [clientInput, setClientInput] = React.useState(undefined);
|
|
8
|
-
const [isTransitioning, startTransition] = React.useTransition();
|
|
9
|
-
// Derived state booleans
|
|
10
|
-
const isIdle = status === 'idle';
|
|
11
|
-
const isLoading = status === 'loading' || isTransitioning;
|
|
12
|
-
const isSuccess = status === 'success';
|
|
13
|
-
const isError = status === 'error';
|
|
14
|
-
const handleSuccess = React.useCallback((result) => {
|
|
15
|
-
setData(result);
|
|
16
|
-
setStatus('success');
|
|
17
|
-
options?.onSuccess?.(result); // Call onSuccess if provided
|
|
18
|
-
options?.onSettled?.(); // Call onSettled if provided
|
|
19
|
-
}, [options]);
|
|
20
|
-
const handleError = React.useCallback((err) => {
|
|
21
|
-
setError(err);
|
|
22
|
-
setStatus('error');
|
|
23
|
-
options?.onError?.(err); // Call onError if provided
|
|
24
|
-
options?.onSettled?.(); // Call onSettled if provided
|
|
25
|
-
}, [options]);
|
|
26
|
-
// Executes the action with the provided input, updating state accordingly
|
|
27
|
-
const execute = React.useCallback((input) => {
|
|
28
|
-
setClientInput(input);
|
|
29
|
-
setStatus('loading');
|
|
30
|
-
setError(null);
|
|
31
|
-
startTransition(() => {
|
|
32
|
-
action(input).then(handleSuccess).catch(handleError);
|
|
33
|
-
});
|
|
34
|
-
}, [action, handleSuccess, handleError]);
|
|
35
|
-
// Asynchronous version of execute for promise-based consumption
|
|
36
|
-
const executeAsync = React.useCallback((input) => {
|
|
37
|
-
return new Promise((resolve, reject) => {
|
|
38
|
-
setClientInput(input);
|
|
39
|
-
setStatus('loading');
|
|
40
|
-
setError(null);
|
|
41
|
-
startTransition(() => {
|
|
42
|
-
action(input)
|
|
43
|
-
.then((res) => {
|
|
44
|
-
handleSuccess(res);
|
|
45
|
-
resolve(res);
|
|
46
|
-
})
|
|
47
|
-
.catch((err) => {
|
|
48
|
-
handleError(err);
|
|
49
|
-
reject(err);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
}, [action, handleSuccess, handleError]);
|
|
54
|
-
// Resets the hook's state to its initial "idle" status
|
|
55
|
-
const reset = React.useCallback(() => {
|
|
56
|
-
setStatus('idle');
|
|
57
|
-
setData(null);
|
|
58
|
-
setError(null);
|
|
59
|
-
setClientInput(undefined);
|
|
60
|
-
}, []);
|
|
61
|
-
// Hook to execute the action automatically on mount
|
|
62
|
-
const useExecute = (input) => {
|
|
63
|
-
useIsomorphicEffect(() => {
|
|
64
|
-
execute(input);
|
|
65
|
-
}, []);
|
|
66
|
-
};
|
|
67
|
-
return {
|
|
68
|
-
// Methods to trigger action
|
|
69
|
-
execute,
|
|
70
|
-
executeAsync,
|
|
71
|
-
reset,
|
|
72
|
-
// Hook for auto execution on mount
|
|
73
|
-
useExecute,
|
|
74
|
-
// Data and error objects
|
|
75
|
-
data,
|
|
76
|
-
error,
|
|
77
|
-
input: clientInput,
|
|
78
|
-
// Status booleans
|
|
79
|
-
isIdle,
|
|
80
|
-
isLoading,
|
|
81
|
-
isSuccess,
|
|
82
|
-
isError,
|
|
83
|
-
};
|
|
84
|
-
};
|
package/dist/hooks/async.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
type AsyncStatus = 'idle' | 'pending' | 'success' | 'error';
|
|
3
|
-
type OnSuccess<TData> = (data: TData) => void | Promise<void>;
|
|
4
|
-
type OnError<TError extends Error = Error> = (error: TError) => void | Promise<void>;
|
|
5
|
-
type OnSettled<TData, TError extends Error = Error> = (data: TData | undefined, error: TError | undefined) => void | Promise<void>;
|
|
6
|
-
interface UseAsyncOptions<TData = unknown, TError extends Error = Error> {
|
|
7
|
-
mode?: 'auto' | 'manual';
|
|
8
|
-
deps?: React.DependencyList;
|
|
9
|
-
onSuccess?: OnSuccess<TData>;
|
|
10
|
-
onError?: OnError<TError>;
|
|
11
|
-
onSettled?: OnSettled<TData, TError>;
|
|
12
|
-
}
|
|
13
|
-
interface UseAsyncReturn<TData, TError extends Error = Error> {
|
|
14
|
-
data: TData | undefined;
|
|
15
|
-
error: TError | undefined;
|
|
16
|
-
status: AsyncStatus;
|
|
17
|
-
isIdle: boolean;
|
|
18
|
-
isPending: boolean;
|
|
19
|
-
isSuccess: boolean;
|
|
20
|
-
isError: boolean;
|
|
21
|
-
execute: () => Promise<TData>;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* A fully typesafe async hook inspired by TanStack Query
|
|
25
|
-
* @param asyncFn - Async function that returns data
|
|
26
|
-
* @param options - Configuration options including callbacks
|
|
27
|
-
* @returns Object with data, error, status and helper methods
|
|
28
|
-
*/
|
|
29
|
-
export declare function useAsync<TData, TError extends Error = Error>(asyncFn: (signal: AbortSignal) => Promise<TData>, options?: UseAsyncOptions<TData, TError>): UseAsyncReturn<TData, TError>;
|
|
30
|
-
export {};
|
package/dist/hooks/async.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
/**
|
|
4
|
-
* A fully typesafe async hook inspired by TanStack Query
|
|
5
|
-
* @param asyncFn - Async function that returns data
|
|
6
|
-
* @param options - Configuration options including callbacks
|
|
7
|
-
* @returns Object with data, error, status and helper methods
|
|
8
|
-
*/
|
|
9
|
-
export function useAsync(asyncFn, options = {}) {
|
|
10
|
-
const { mode = 'manual', deps, onSuccess, onError, onSettled } = options;
|
|
11
|
-
const [data, setData] = React.useState(undefined);
|
|
12
|
-
const [error, setError] = React.useState(undefined);
|
|
13
|
-
const [status, setStatus] = React.useState('idle');
|
|
14
|
-
const abortControllerRef = React.useRef(null);
|
|
15
|
-
const memoizedOnSuccess = React.useCallback(onSuccess || (() => { }), [
|
|
16
|
-
onSuccess,
|
|
17
|
-
]);
|
|
18
|
-
const memoizedOnError = React.useCallback(onError || (() => { }), [onError]);
|
|
19
|
-
const memoizedOnSettled = React.useCallback(onSettled || (() => { }), [
|
|
20
|
-
onSettled,
|
|
21
|
-
]);
|
|
22
|
-
const execute = React.useCallback(async () => {
|
|
23
|
-
if (abortControllerRef.current) {
|
|
24
|
-
abortControllerRef.current.abort();
|
|
25
|
-
}
|
|
26
|
-
abortControllerRef.current = new AbortController();
|
|
27
|
-
const signal = abortControllerRef.current.signal;
|
|
28
|
-
setStatus('pending');
|
|
29
|
-
setError(undefined);
|
|
30
|
-
try {
|
|
31
|
-
const result = await asyncFn(signal);
|
|
32
|
-
if (!signal.aborted) {
|
|
33
|
-
setData(result);
|
|
34
|
-
setStatus('success');
|
|
35
|
-
await memoizedOnSuccess(result);
|
|
36
|
-
await memoizedOnSettled(result, undefined);
|
|
37
|
-
}
|
|
38
|
-
return result;
|
|
39
|
-
}
|
|
40
|
-
catch (err) {
|
|
41
|
-
if (err instanceof Error && err.name === 'AbortError') {
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
44
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
45
|
-
const typedError = error;
|
|
46
|
-
if (!signal.aborted) {
|
|
47
|
-
setError(typedError);
|
|
48
|
-
setStatus('error');
|
|
49
|
-
await memoizedOnError(typedError);
|
|
50
|
-
await memoizedOnSettled(undefined, typedError);
|
|
51
|
-
}
|
|
52
|
-
throw error;
|
|
53
|
-
}
|
|
54
|
-
}, [asyncFn, memoizedOnSuccess, memoizedOnError, memoizedOnSettled]);
|
|
55
|
-
React.useEffect(() => {
|
|
56
|
-
if (mode === 'auto' && !deps) {
|
|
57
|
-
execute();
|
|
58
|
-
}
|
|
59
|
-
}, [asyncFn, mode, execute, deps]);
|
|
60
|
-
React.useEffect(() => {
|
|
61
|
-
if (deps && mode === 'auto') {
|
|
62
|
-
execute();
|
|
63
|
-
}
|
|
64
|
-
}, deps ? deps : []);
|
|
65
|
-
React.useEffect(() => {
|
|
66
|
-
return () => {
|
|
67
|
-
if (abortControllerRef.current) {
|
|
68
|
-
abortControllerRef.current.abort();
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
}, []);
|
|
72
|
-
return {
|
|
73
|
-
data,
|
|
74
|
-
error,
|
|
75
|
-
status,
|
|
76
|
-
isIdle: status === 'idle',
|
|
77
|
-
isPending: status === 'pending',
|
|
78
|
-
isSuccess: status === 'success',
|
|
79
|
-
isError: status === 'error',
|
|
80
|
-
execute,
|
|
81
|
-
};
|
|
82
|
-
}
|