ngx-com 0.0.1 → 0.0.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.
Files changed (82) hide show
  1. package/fesm2022/ngx-com-components-avatar.mjs +772 -0
  2. package/fesm2022/ngx-com-components-avatar.mjs.map +1 -0
  3. package/fesm2022/ngx-com-components-badge.mjs +138 -0
  4. package/fesm2022/ngx-com-components-badge.mjs.map +1 -0
  5. package/fesm2022/ngx-com-components-button.mjs +146 -0
  6. package/fesm2022/ngx-com-components-button.mjs.map +1 -0
  7. package/fesm2022/ngx-com-components-calendar.mjs +5046 -0
  8. package/fesm2022/ngx-com-components-calendar.mjs.map +1 -0
  9. package/fesm2022/ngx-com-components-card.mjs +590 -0
  10. package/fesm2022/ngx-com-components-card.mjs.map +1 -0
  11. package/fesm2022/ngx-com-components-checkbox.mjs +344 -0
  12. package/fesm2022/ngx-com-components-checkbox.mjs.map +1 -0
  13. package/fesm2022/ngx-com-components-collapsible.mjs +612 -0
  14. package/fesm2022/ngx-com-components-collapsible.mjs.map +1 -0
  15. package/fesm2022/ngx-com-components-confirm.mjs +562 -0
  16. package/fesm2022/ngx-com-components-confirm.mjs.map +1 -0
  17. package/fesm2022/ngx-com-components-dropdown-testing.mjs +255 -0
  18. package/fesm2022/ngx-com-components-dropdown-testing.mjs.map +1 -0
  19. package/fesm2022/ngx-com-components-dropdown.mjs +2692 -0
  20. package/fesm2022/ngx-com-components-dropdown.mjs.map +1 -0
  21. package/fesm2022/ngx-com-components-empty-state.mjs +382 -0
  22. package/fesm2022/ngx-com-components-empty-state.mjs.map +1 -0
  23. package/fesm2022/ngx-com-components-form-field.mjs +924 -0
  24. package/fesm2022/ngx-com-components-form-field.mjs.map +1 -0
  25. package/fesm2022/ngx-com-components-icon.mjs +183 -0
  26. package/fesm2022/ngx-com-components-icon.mjs.map +1 -0
  27. package/fesm2022/ngx-com-components-item.mjs +578 -0
  28. package/fesm2022/ngx-com-components-item.mjs.map +1 -0
  29. package/fesm2022/ngx-com-components-menu.mjs +1200 -0
  30. package/fesm2022/ngx-com-components-menu.mjs.map +1 -0
  31. package/fesm2022/ngx-com-components-paginator.mjs +823 -0
  32. package/fesm2022/ngx-com-components-paginator.mjs.map +1 -0
  33. package/fesm2022/ngx-com-components-popover.mjs +901 -0
  34. package/fesm2022/ngx-com-components-popover.mjs.map +1 -0
  35. package/fesm2022/ngx-com-components-radio.mjs +621 -0
  36. package/fesm2022/ngx-com-components-radio.mjs.map +1 -0
  37. package/fesm2022/ngx-com-components-segmented-control.mjs +538 -0
  38. package/fesm2022/ngx-com-components-segmented-control.mjs.map +1 -0
  39. package/fesm2022/ngx-com-components-sort.mjs +368 -0
  40. package/fesm2022/ngx-com-components-sort.mjs.map +1 -0
  41. package/fesm2022/ngx-com-components-spinner.mjs +189 -0
  42. package/fesm2022/ngx-com-components-spinner.mjs.map +1 -0
  43. package/fesm2022/ngx-com-components-tabs.mjs +1522 -0
  44. package/fesm2022/ngx-com-components-tabs.mjs.map +1 -0
  45. package/fesm2022/ngx-com-components-tooltip.mjs +625 -0
  46. package/fesm2022/ngx-com-components-tooltip.mjs.map +1 -0
  47. package/fesm2022/ngx-com-components.mjs +17 -0
  48. package/fesm2022/ngx-com-components.mjs.map +1 -0
  49. package/fesm2022/ngx-com-tokens.mjs +12 -0
  50. package/fesm2022/ngx-com-tokens.mjs.map +1 -0
  51. package/fesm2022/ngx-com-utils.mjs +601 -0
  52. package/fesm2022/ngx-com-utils.mjs.map +1 -0
  53. package/fesm2022/ngx-com.mjs +9 -23
  54. package/fesm2022/ngx-com.mjs.map +1 -1
  55. package/package.json +105 -1
  56. package/types/ngx-com-components-avatar.d.ts +409 -0
  57. package/types/ngx-com-components-badge.d.ts +97 -0
  58. package/types/ngx-com-components-button.d.ts +69 -0
  59. package/types/ngx-com-components-calendar.d.ts +1665 -0
  60. package/types/ngx-com-components-card.d.ts +373 -0
  61. package/types/ngx-com-components-checkbox.d.ts +116 -0
  62. package/types/ngx-com-components-collapsible.d.ts +379 -0
  63. package/types/ngx-com-components-confirm.d.ts +160 -0
  64. package/types/ngx-com-components-dropdown-testing.d.ts +116 -0
  65. package/types/ngx-com-components-dropdown.d.ts +938 -0
  66. package/types/ngx-com-components-empty-state.d.ts +269 -0
  67. package/types/ngx-com-components-form-field.d.ts +531 -0
  68. package/types/ngx-com-components-icon.d.ts +94 -0
  69. package/types/ngx-com-components-item.d.ts +336 -0
  70. package/types/ngx-com-components-menu.d.ts +479 -0
  71. package/types/ngx-com-components-paginator.d.ts +265 -0
  72. package/types/ngx-com-components-popover.d.ts +309 -0
  73. package/types/ngx-com-components-radio.d.ts +258 -0
  74. package/types/ngx-com-components-segmented-control.d.ts +274 -0
  75. package/types/ngx-com-components-sort.d.ts +133 -0
  76. package/types/ngx-com-components-spinner.d.ts +120 -0
  77. package/types/ngx-com-components-tabs.d.ts +396 -0
  78. package/types/ngx-com-components-tooltip.d.ts +200 -0
  79. package/types/ngx-com-components.d.ts +12 -0
  80. package/types/ngx-com-tokens.d.ts +7 -0
  81. package/types/ngx-com-utils.d.ts +424 -0
  82. package/types/ngx-com.d.ts +10 -7
@@ -0,0 +1,601 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ /**
5
+ * Merges and deduplicates CSS class names using clsx and tailwind-merge.
6
+ * Tailwind-merge ensures conflicting utility classes are resolved correctly.
7
+ *
8
+ * @param inputs - Class values to merge (strings, arrays, objects, or falsy values)
9
+ * @returns A single string of merged, deduplicated class names
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * mergeClasses('px-2 py-1', 'px-4'); // 'py-1 px-4'
14
+ * mergeClasses('text-sm', condition && 'text-lg'); // 'text-lg' if condition is true
15
+ * ```
16
+ */
17
+ function mergeClasses(...inputs) {
18
+ return twMerge(clsx(inputs));
19
+ }
20
+
21
+ /**
22
+ * Creates a debounced version of the provided function that delays invoking
23
+ * until after the specified wait time has elapsed since the last call.
24
+ *
25
+ * The debounced function includes `cancel()` to abort pending calls,
26
+ * `flush()` to immediately execute pending calls, and `pending()` to check status.
27
+ *
28
+ * @template T - The type of the function to debounce.
29
+ * @param fn - The function to debounce.
30
+ * @param optionsOrWait - Wait time in ms or options object.
31
+ * @returns The debounced function with control methods.
32
+ *
33
+ * @example
34
+ * // Basic usage with default 300ms delay
35
+ * const debouncedSearch = debounce((query: string) => {
36
+ * console.log('Searching for:', query);
37
+ * });
38
+ * debouncedSearch('hello'); // Logs after 300ms if no subsequent calls
39
+ *
40
+ * @example
41
+ * // With custom wait time
42
+ * const debouncedSave = debounce(
43
+ * (data: { id: number; value: string }) => saveToServer(data),
44
+ * 500
45
+ * );
46
+ *
47
+ * @example
48
+ * // With options object and leading edge execution
49
+ * const debouncedClick = debounce(
50
+ * () => handleClick(),
51
+ * { wait: 200, leading: true }
52
+ * );
53
+ *
54
+ * @example
55
+ * // Cleanup in Angular component
56
+ * export class SearchComponent {
57
+ * private debouncedSearch = debounce((q: string) => this.search(q), 300);
58
+ *
59
+ * ngOnDestroy(): void {
60
+ * this.debouncedSearch.cancel();
61
+ * }
62
+ * }
63
+ */
64
+ function debounce(fn, optionsOrWait = 300) {
65
+ const options = typeof optionsOrWait === 'number' ? { wait: optionsOrWait } : optionsOrWait;
66
+ const wait = options.wait ?? 300;
67
+ const leading = options.leading ?? false;
68
+ let timerId = null;
69
+ let pendingArgs = null;
70
+ let leadingInvoked = false;
71
+ const invoke = () => {
72
+ if (pendingArgs !== null) {
73
+ fn(...pendingArgs);
74
+ pendingArgs = null;
75
+ }
76
+ leadingInvoked = false;
77
+ };
78
+ const debounced = (...args) => {
79
+ pendingArgs = args;
80
+ if (leading && !leadingInvoked) {
81
+ leadingInvoked = true;
82
+ invoke();
83
+ }
84
+ if (timerId !== null) {
85
+ clearTimeout(timerId);
86
+ }
87
+ timerId = setTimeout(() => {
88
+ timerId = null;
89
+ if (!leading || pendingArgs !== null) {
90
+ invoke();
91
+ }
92
+ }, wait);
93
+ };
94
+ debounced.cancel = () => {
95
+ if (timerId !== null) {
96
+ clearTimeout(timerId);
97
+ timerId = null;
98
+ }
99
+ pendingArgs = null;
100
+ leadingInvoked = false;
101
+ };
102
+ debounced.flush = () => {
103
+ if (timerId !== null) {
104
+ clearTimeout(timerId);
105
+ timerId = null;
106
+ invoke();
107
+ }
108
+ };
109
+ debounced.pending = () => timerId !== null;
110
+ return debounced;
111
+ }
112
+
113
+ /**
114
+ * Creates a throttled version of the provided function that only invokes
115
+ * at most once per the specified wait period.
116
+ *
117
+ * The function is invoked on the leading edge (immediately on first call)
118
+ * and then at most once per wait period for subsequent calls.
119
+ *
120
+ * @template T - The type of the function to throttle.
121
+ * @param fn - The function to throttle.
122
+ * @param wait - The minimum time between invocations in milliseconds.
123
+ * @returns The throttled function with a cancel method.
124
+ *
125
+ * @example
126
+ * // Basic usage - scroll handler
127
+ * const throttledScroll = throttle(() => {
128
+ * console.log('Scroll position:', window.scrollY);
129
+ * }, 100);
130
+ * window.addEventListener('scroll', throttledScroll);
131
+ *
132
+ * @example
133
+ * // With cleanup
134
+ * export class ScrollComponent {
135
+ * private throttledHandler = throttle((e: Event) => this.handleScroll(e), 100);
136
+ *
137
+ * ngOnDestroy(): void {
138
+ * this.throttledHandler.cancel();
139
+ * }
140
+ * }
141
+ */
142
+ function throttle(fn, wait) {
143
+ let lastCallTime = 0;
144
+ let timerId = null;
145
+ let pendingArgs = null;
146
+ const invoke = (args) => {
147
+ lastCallTime = Date.now();
148
+ fn(...args);
149
+ };
150
+ const throttled = (...args) => {
151
+ const now = Date.now();
152
+ const remaining = wait - (now - lastCallTime);
153
+ if (remaining <= 0) {
154
+ if (timerId !== null) {
155
+ clearTimeout(timerId);
156
+ timerId = null;
157
+ }
158
+ invoke(args);
159
+ }
160
+ else {
161
+ pendingArgs = args;
162
+ if (timerId === null) {
163
+ timerId = setTimeout(() => {
164
+ timerId = null;
165
+ if (pendingArgs !== null) {
166
+ invoke(pendingArgs);
167
+ pendingArgs = null;
168
+ }
169
+ }, remaining);
170
+ }
171
+ }
172
+ };
173
+ throttled.cancel = () => {
174
+ if (timerId !== null) {
175
+ clearTimeout(timerId);
176
+ timerId = null;
177
+ }
178
+ pendingArgs = null;
179
+ };
180
+ return throttled;
181
+ }
182
+
183
+ /**
184
+ * Retries an async function with configurable attempts, delay, and backoff.
185
+ *
186
+ * @template T - The return type of the async function.
187
+ * @param fn - The async function to retry.
188
+ * @param options - Retry configuration.
189
+ * @returns The result of the successful call.
190
+ * @throws The last error if all attempts fail.
191
+ *
192
+ * @example
193
+ * // Basic retry with 3 attempts
194
+ * const data = await retry(
195
+ * () => fetch('/api/data').then(r => r.json()),
196
+ * { attempts: 3, delay: 1000 }
197
+ * );
198
+ *
199
+ * @example
200
+ * // With exponential backoff
201
+ * const result = await retry(
202
+ * () => unreliableApiCall(),
203
+ * { attempts: 5, delay: 100, backoff: 2 } // delays: 100, 200, 400, 800, 1600
204
+ * );
205
+ *
206
+ * @example
207
+ * // With conditional retry
208
+ * const result = await retry(
209
+ * () => apiCall(),
210
+ * {
211
+ * attempts: 3,
212
+ * delay: 500,
213
+ * shouldRetry: (err) => err instanceof NetworkError
214
+ * }
215
+ * );
216
+ */
217
+ async function retry(fn, options) {
218
+ const { attempts, delay, backoff = 1, shouldRetry } = options;
219
+ let lastError;
220
+ let currentDelay = delay;
221
+ for (let attempt = 1; attempt <= attempts; attempt++) {
222
+ try {
223
+ return await fn();
224
+ }
225
+ catch (error) {
226
+ lastError = error;
227
+ if (shouldRetry && !shouldRetry(error)) {
228
+ throw error;
229
+ }
230
+ if (attempt < attempts) {
231
+ await new Promise(resolve => setTimeout(resolve, currentDelay));
232
+ currentDelay *= backoff;
233
+ }
234
+ }
235
+ }
236
+ throw lastError;
237
+ }
238
+
239
+ /**
240
+ * Creates a new object with only the specified keys from the source object.
241
+ *
242
+ * @template T - The type of the source object.
243
+ * @template K - The keys to pick.
244
+ * @param obj - The source object.
245
+ * @param keys - The keys to pick from the object.
246
+ * @returns A new object containing only the specified keys.
247
+ *
248
+ * @example
249
+ * const user = { id: 1, name: 'John', email: 'john@example.com', age: 30 };
250
+ * pick(user, ['id', 'name']); // { id: 1, name: 'John' }
251
+ *
252
+ * @example
253
+ * const config = { host: 'localhost', port: 3000, debug: true };
254
+ * pick(config, ['host', 'port']); // { host: 'localhost', port: 3000 }
255
+ */
256
+ function pick(obj, keys) {
257
+ const result = {};
258
+ for (const key of keys) {
259
+ if (key in obj) {
260
+ result[key] = obj[key];
261
+ }
262
+ }
263
+ return result;
264
+ }
265
+
266
+ /**
267
+ * Creates a new object with the specified keys omitted from the source object.
268
+ *
269
+ * @template T - The type of the source object.
270
+ * @template K - The keys to omit.
271
+ * @param obj - The source object.
272
+ * @param keys - The keys to omit from the object.
273
+ * @returns A new object without the specified keys.
274
+ *
275
+ * @example
276
+ * const user = { id: 1, name: 'John', password: 'secret', email: 'john@example.com' };
277
+ * omit(user, ['password']); // { id: 1, name: 'John', email: 'john@example.com' }
278
+ *
279
+ * @example
280
+ * const config = { host: 'localhost', port: 3000, debug: true };
281
+ * omit(config, ['debug']); // { host: 'localhost', port: 3000 }
282
+ */
283
+ function omit(obj, keys) {
284
+ const result = { ...obj };
285
+ for (const key of keys) {
286
+ delete result[key];
287
+ }
288
+ return result;
289
+ }
290
+
291
+ /**
292
+ * Creates a deep clone of a value, handling objects, arrays, Date, and RegExp.
293
+ * Note: Does not handle circular references, functions, or special objects like Map, Set.
294
+ * For complex cloning needs, consider using `structuredClone()`.
295
+ *
296
+ * @template T - The type of the value to clone.
297
+ * @param value - The value to deep clone.
298
+ * @returns A deep clone of the value.
299
+ *
300
+ * @example
301
+ * const original = { a: 1, b: { c: 2, d: [3, 4] } };
302
+ * const cloned = deepClone(original);
303
+ * cloned.b.c = 5;
304
+ * console.log(original.b.c); // 2 (unchanged)
305
+ *
306
+ * @example
307
+ * const arr = [{ id: 1 }, { id: 2 }];
308
+ * const clonedArr = deepClone(arr);
309
+ * clonedArr[0].id = 99;
310
+ * console.log(arr[0].id); // 1 (unchanged)
311
+ */
312
+ function deepClone(value) {
313
+ if (value === null || typeof value !== 'object') {
314
+ return value;
315
+ }
316
+ if (value instanceof Date) {
317
+ return new Date(value.getTime());
318
+ }
319
+ if (value instanceof RegExp) {
320
+ return new RegExp(value.source, value.flags);
321
+ }
322
+ if (Array.isArray(value)) {
323
+ return value.map(item => deepClone(item));
324
+ }
325
+ // At this point, value is guaranteed to be a plain object
326
+ const result = {};
327
+ for (const key of Object.keys(value)) {
328
+ result[key] = deepClone(value[key]);
329
+ }
330
+ return result;
331
+ }
332
+
333
+ /**
334
+ * Checks if a value is a plain object (not null, not an array).
335
+ *
336
+ * @param value - The value to check.
337
+ * @returns `true` if value is a plain object; otherwise, `false`.
338
+ *
339
+ * @example
340
+ * isPlainObject({ a: 1 }); // true
341
+ * isPlainObject([1, 2]); // false
342
+ * isPlainObject(null); // false
343
+ */
344
+ function isPlainObject(value) {
345
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
346
+ }
347
+
348
+ /**
349
+ * Deep merges multiple source objects into a new object.
350
+ * Later sources override earlier ones. Arrays are replaced, not merged.
351
+ *
352
+ * @template T - The type of the resulting object.
353
+ * @param sources - The source objects to merge.
354
+ * @returns A new deeply merged object.
355
+ *
356
+ * @example
357
+ * const defaults = { theme: { color: 'blue', size: 'md' }, debug: false };
358
+ * const userConfig = { theme: { color: 'red' }, debug: true };
359
+ * deepMerge(defaults, userConfig);
360
+ * // { theme: { color: 'red', size: 'md' }, debug: true }
361
+ *
362
+ * @example
363
+ * const a = { arr: [1, 2], nested: { x: 1 } };
364
+ * const b = { arr: [3, 4], nested: { y: 2 } };
365
+ * deepMerge(a, b);
366
+ * // { arr: [3, 4], nested: { x: 1, y: 2 } } - arrays are replaced
367
+ */
368
+ function deepMerge(...sources) {
369
+ const result = {};
370
+ for (const source of sources) {
371
+ if (typeof source !== 'object' || source === null) {
372
+ continue;
373
+ }
374
+ for (const key of Object.keys(source)) {
375
+ const sourceValue = source[key];
376
+ const resultValue = result[key];
377
+ if (Array.isArray(sourceValue)) {
378
+ result[key] = deepClone(sourceValue);
379
+ }
380
+ else if (isPlainObject(sourceValue) && isPlainObject(resultValue)) {
381
+ result[key] = deepMerge(resultValue, sourceValue);
382
+ }
383
+ else if (isPlainObject(sourceValue)) {
384
+ result[key] = deepClone(sourceValue);
385
+ }
386
+ else {
387
+ result[key] = sourceValue;
388
+ }
389
+ }
390
+ }
391
+ return result;
392
+ }
393
+
394
+ /**
395
+ * Performs a deep equality comparison between two values.
396
+ * Handles primitives, objects, arrays, Date, and RegExp.
397
+ *
398
+ * @template T - The type of values to compare.
399
+ * @param a - The first value.
400
+ * @param b - The second value.
401
+ * @returns `true` if values are deeply equal; otherwise, `false`.
402
+ *
403
+ * @example
404
+ * deepEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } }); // true
405
+ * deepEqual({ a: 1 }, { a: 2 }); // false
406
+ * deepEqual([1, [2, 3]], [1, [2, 3]]); // true
407
+ * deepEqual([1, 2], [1, 2, 3]); // false
408
+ * deepEqual(new Date('2024-01-01'), new Date('2024-01-01')); // true
409
+ */
410
+ function deepEqual(a, b) {
411
+ if (a === b) {
412
+ return true;
413
+ }
414
+ if (a === null || b === null) {
415
+ return a === b;
416
+ }
417
+ if (typeof a !== typeof b) {
418
+ return false;
419
+ }
420
+ if (typeof a !== 'object') {
421
+ return a === b;
422
+ }
423
+ if (a instanceof Date && b instanceof Date) {
424
+ return a.getTime() === b.getTime();
425
+ }
426
+ if (a instanceof RegExp && b instanceof RegExp) {
427
+ return a.source === b.source && a.flags === b.flags;
428
+ }
429
+ if (Array.isArray(a) && Array.isArray(b)) {
430
+ if (a.length !== b.length) {
431
+ return false;
432
+ }
433
+ for (let i = 0; i < a.length; i++) {
434
+ if (!deepEqual(a[i], b[i])) {
435
+ return false;
436
+ }
437
+ }
438
+ return true;
439
+ }
440
+ if (Array.isArray(a) !== Array.isArray(b)) {
441
+ return false;
442
+ }
443
+ if (isPlainObject(a) && isPlainObject(b)) {
444
+ const keysA = Object.keys(a);
445
+ const keysB = Object.keys(b);
446
+ if (keysA.length !== keysB.length) {
447
+ return false;
448
+ }
449
+ for (const key of keysA) {
450
+ if (!Object.prototype.hasOwnProperty.call(b, key)) {
451
+ return false;
452
+ }
453
+ if (!deepEqual(a[key], b[key])) {
454
+ return false;
455
+ }
456
+ }
457
+ return true;
458
+ }
459
+ return false;
460
+ }
461
+
462
+ /**
463
+ * Resolves a nested path in an object, returning the value at the specified path.
464
+ *
465
+ * This function splits the provided `path` into keys and iterates through the object to access the
466
+ * corresponding value at each key. If any part of the path is undefined or if the path leads to a non-object,
467
+ * it returns `undefined`.
468
+ *
469
+ * @template T - The type of the object.
470
+ * @template P - The dot-separated string path type.
471
+ * @param obj - The object to resolve the path within.
472
+ * @param path - The dot-separated string representing the path to the value.
473
+ * @returns The value at the resolved path, or `undefined` if any part of the path is invalid.
474
+ *
475
+ * @example
476
+ * const obj = { user: { profile: { name: 'John Doe' } } };
477
+ * const result = resolvePath(obj, 'user.profile.name');
478
+ * console.log(result); // 'John Doe'
479
+ *
480
+ * @example
481
+ * const result2 = resolvePath(obj, 'user.address.city');
482
+ * console.log(result2); // undefined
483
+ */
484
+ function resolvePath(obj, path) {
485
+ const keys = path.split('.');
486
+ let current = obj;
487
+ for (const key of keys) {
488
+ if (current && typeof current === 'object' && key in current) {
489
+ current = current[key];
490
+ }
491
+ else {
492
+ return undefined;
493
+ }
494
+ }
495
+ return current;
496
+ }
497
+
498
+ /**
499
+ * Splits an array into chunks of the specified size.
500
+ *
501
+ * @template T - The type of array elements.
502
+ * @param array - The array to split.
503
+ * @param size - The size of each chunk (must be positive).
504
+ * @returns An array of chunks.
505
+ *
506
+ * @example
507
+ * chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
508
+ * chunk([1, 2, 3, 4], 2); // [[1, 2], [3, 4]]
509
+ * chunk([1, 2, 3], 5); // [[1, 2, 3]]
510
+ * chunk([], 2); // []
511
+ */
512
+ function chunk(array, size) {
513
+ if (size <= 0) {
514
+ throw new Error('Chunk size must be a positive number');
515
+ }
516
+ const result = [];
517
+ for (let i = 0; i < array.length; i += size) {
518
+ result.push(array.slice(i, i + size));
519
+ }
520
+ return result;
521
+ }
522
+
523
+ /**
524
+ * Splits an array into two groups based on a predicate function.
525
+ * The first array contains elements that satisfy the predicate,
526
+ * the second contains elements that don't.
527
+ *
528
+ * @template T - The type of array elements.
529
+ * @param array - The array to partition.
530
+ * @param predicate - The function to test each element.
531
+ * @returns A tuple of [truthy, falsy] arrays.
532
+ *
533
+ * @example
534
+ * const numbers = [1, 2, 3, 4, 5, 6];
535
+ * partition(numbers, n => n % 2 === 0); // [[2, 4, 6], [1, 3, 5]]
536
+ *
537
+ * @example
538
+ * const users = [
539
+ * { name: 'Alice', active: true },
540
+ * { name: 'Bob', active: false },
541
+ * { name: 'Charlie', active: true }
542
+ * ];
543
+ * partition(users, u => u.active);
544
+ * // [[{ name: 'Alice', active: true }, { name: 'Charlie', active: true }], [{ name: 'Bob', active: false }]]
545
+ */
546
+ function partition(array, predicate) {
547
+ const truthy = [];
548
+ const falsy = [];
549
+ for (const item of array) {
550
+ if (predicate(item)) {
551
+ truthy.push(item);
552
+ }
553
+ else {
554
+ falsy.push(item);
555
+ }
556
+ }
557
+ return [truthy, falsy];
558
+ }
559
+
560
+ /**
561
+ * Groups an array of items based on a provided predicate function.
562
+ *
563
+ * @template K - The type of keys produced by the predicate, typically a string, number, or symbol.
564
+ * @template V - The type of items in the collection.
565
+ * @param collection - The array of items to be grouped.
566
+ * @param predicate - A function that takes an item and returns a key by which to group the item.
567
+ * @returns An object where each key is a group identifier produced by the predicate,
568
+ * and the value is an array of items belonging to that group.
569
+ *
570
+ * @example
571
+ * // Group an array of people by their age
572
+ * const people = [{ name: 'Alice', age: 25 }, { name: 'Bob', age: 25 }, { name: 'Charlie', age: 30 }];
573
+ * const groupedByAge = groupBy(people, person => person.age);
574
+ * // Output:
575
+ * // {
576
+ * // 25: [{ name: 'Alice', age: 25 }, { name: 'Bob', age: 25 }],
577
+ * // 30: [{ name: 'Charlie', age: 30 }]
578
+ * // }
579
+ */
580
+ function groupBy(collection, predicate) {
581
+ return collection.reduce((groups, item) => {
582
+ const groupKey = predicate(item);
583
+ if (!groups[groupKey]) {
584
+ groups[groupKey] = [];
585
+ }
586
+ groups[groupKey].push(item);
587
+ return groups;
588
+ }, {});
589
+ }
590
+
591
+ /**
592
+ * ngx-com/utils
593
+ * Utility function exports for ngx-com library
594
+ */
595
+
596
+ /**
597
+ * Generated bundle index. Do not edit.
598
+ */
599
+
600
+ export { chunk, debounce, deepClone, deepEqual, deepMerge, groupBy, isPlainObject, mergeClasses, omit, partition, pick, resolvePath, retry, throttle };
601
+ //# sourceMappingURL=ngx-com-utils.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngx-com-utils.mjs","sources":["../../../projects/com/utils/src/merge-classes.ts","../../../projects/com/utils/src/debounce.ts","../../../projects/com/utils/src/throttle.ts","../../../projects/com/utils/src/retry.ts","../../../projects/com/utils/src/pick.ts","../../../projects/com/utils/src/omit.ts","../../../projects/com/utils/src/deep-clone.ts","../../../projects/com/utils/src/is-plain-object.ts","../../../projects/com/utils/src/deep-merge.ts","../../../projects/com/utils/src/deep-equal.ts","../../../projects/com/utils/src/resolve-path.ts","../../../projects/com/utils/src/chunk.ts","../../../projects/com/utils/src/partition.ts","../../../projects/com/utils/src/group-by.ts","../../../projects/com/utils/src/index.ts","../../../projects/com/utils/src/ngx-com-utils.ts"],"sourcesContent":["import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merges and deduplicates CSS class names using clsx and tailwind-merge.\n * Tailwind-merge ensures conflicting utility classes are resolved correctly.\n *\n * @param inputs - Class values to merge (strings, arrays, objects, or falsy values)\n * @returns A single string of merged, deduplicated class names\n *\n * @example\n * ```ts\n * mergeClasses('px-2 py-1', 'px-4'); // 'py-1 px-4'\n * mergeClasses('text-sm', condition && 'text-lg'); // 'text-lg' if condition is true\n * ```\n */\nexport function mergeClasses(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * A debounced function wrapper with cancel and flush capabilities.\n *\n * @template T - The type of the original function.\n */\nexport interface DebouncedFn<T extends (...args: never[]) => void> {\n /**\n * Calls the debounced function. The actual execution is delayed until\n * the specified wait time has passed without another call.\n */\n (...args: Parameters<T>): void;\n\n /**\n * Cancels any pending debounced call.\n * Useful for cleanup in component destruction or effect cleanup.\n */\n cancel(): void;\n\n /**\n * Immediately executes the pending debounced call if one exists.\n * Does nothing if no call is pending.\n */\n flush(): void;\n\n /**\n * Returns whether there is a pending debounced call.\n */\n pending(): boolean;\n}\n\n/**\n * Configuration options for the debounce function.\n */\nexport interface DebounceOptions {\n /**\n * The number of milliseconds to delay execution.\n * @default 300\n */\n readonly wait?: number;\n\n /**\n * If `true`, the function is invoked on the leading edge of the timeout\n * instead of the trailing edge.\n * @default false\n */\n readonly leading?: boolean;\n}\n\n/**\n * Creates a debounced version of the provided function that delays invoking\n * until after the specified wait time has elapsed since the last call.\n *\n * The debounced function includes `cancel()` to abort pending calls,\n * `flush()` to immediately execute pending calls, and `pending()` to check status.\n *\n * @template T - The type of the function to debounce.\n * @param fn - The function to debounce.\n * @param optionsOrWait - Wait time in ms or options object.\n * @returns The debounced function with control methods.\n *\n * @example\n * // Basic usage with default 300ms delay\n * const debouncedSearch = debounce((query: string) => {\n * console.log('Searching for:', query);\n * });\n * debouncedSearch('hello'); // Logs after 300ms if no subsequent calls\n *\n * @example\n * // With custom wait time\n * const debouncedSave = debounce(\n * (data: { id: number; value: string }) => saveToServer(data),\n * 500\n * );\n *\n * @example\n * // With options object and leading edge execution\n * const debouncedClick = debounce(\n * () => handleClick(),\n * { wait: 200, leading: true }\n * );\n *\n * @example\n * // Cleanup in Angular component\n * export class SearchComponent {\n * private debouncedSearch = debounce((q: string) => this.search(q), 300);\n *\n * ngOnDestroy(): void {\n * this.debouncedSearch.cancel();\n * }\n * }\n */\nexport function debounce<T extends (...args: never[]) => void>(\n fn: T,\n optionsOrWait: number | DebounceOptions = 300,\n): DebouncedFn<T> {\n const options: DebounceOptions =\n typeof optionsOrWait === 'number' ? { wait: optionsOrWait } : optionsOrWait;\n\n const wait = options.wait ?? 300;\n const leading = options.leading ?? false;\n\n let timerId: ReturnType<typeof setTimeout> | null = null;\n let pendingArgs: Parameters<T> | null = null;\n let leadingInvoked = false;\n\n const invoke = (): void => {\n if (pendingArgs !== null) {\n fn(...pendingArgs);\n pendingArgs = null;\n }\n leadingInvoked = false;\n };\n\n const debounced = (...args: Parameters<T>): void => {\n pendingArgs = args;\n\n if (leading && !leadingInvoked) {\n leadingInvoked = true;\n invoke();\n }\n\n if (timerId !== null) {\n clearTimeout(timerId);\n }\n\n timerId = setTimeout(() => {\n timerId = null;\n if (!leading || pendingArgs !== null) {\n invoke();\n }\n }, wait);\n };\n\n debounced.cancel = (): void => {\n if (timerId !== null) {\n clearTimeout(timerId);\n timerId = null;\n }\n pendingArgs = null;\n leadingInvoked = false;\n };\n\n debounced.flush = (): void => {\n if (timerId !== null) {\n clearTimeout(timerId);\n timerId = null;\n invoke();\n }\n };\n\n debounced.pending = (): boolean => timerId !== null;\n\n return debounced;\n}\n","/**\n * A throttled function wrapper with cancel capability.\n *\n * @template T - The type of the original function.\n */\nexport interface ThrottledFn<T extends (...args: never[]) => void> {\n /**\n * Calls the throttled function. Execution is rate-limited to at most\n * once per the specified wait period.\n */\n (...args: Parameters<T>): void;\n\n /**\n * Cancels any pending throttled call.\n * Useful for cleanup in component destruction.\n */\n cancel(): void;\n}\n\n/**\n * Creates a throttled version of the provided function that only invokes\n * at most once per the specified wait period.\n *\n * The function is invoked on the leading edge (immediately on first call)\n * and then at most once per wait period for subsequent calls.\n *\n * @template T - The type of the function to throttle.\n * @param fn - The function to throttle.\n * @param wait - The minimum time between invocations in milliseconds.\n * @returns The throttled function with a cancel method.\n *\n * @example\n * // Basic usage - scroll handler\n * const throttledScroll = throttle(() => {\n * console.log('Scroll position:', window.scrollY);\n * }, 100);\n * window.addEventListener('scroll', throttledScroll);\n *\n * @example\n * // With cleanup\n * export class ScrollComponent {\n * private throttledHandler = throttle((e: Event) => this.handleScroll(e), 100);\n *\n * ngOnDestroy(): void {\n * this.throttledHandler.cancel();\n * }\n * }\n */\nexport function throttle<T extends (...args: never[]) => void>(\n fn: T,\n wait: number,\n): ThrottledFn<T> {\n let lastCallTime = 0;\n let timerId: ReturnType<typeof setTimeout> | null = null;\n let pendingArgs: Parameters<T> | null = null;\n\n const invoke = (args: Parameters<T>): void => {\n lastCallTime = Date.now();\n fn(...args);\n };\n\n const throttled = (...args: Parameters<T>): void => {\n const now = Date.now();\n const remaining = wait - (now - lastCallTime);\n\n if (remaining <= 0) {\n if (timerId !== null) {\n clearTimeout(timerId);\n timerId = null;\n }\n invoke(args);\n } else {\n pendingArgs = args;\n if (timerId === null) {\n timerId = setTimeout(() => {\n timerId = null;\n if (pendingArgs !== null) {\n invoke(pendingArgs);\n pendingArgs = null;\n }\n }, remaining);\n }\n }\n };\n\n throttled.cancel = (): void => {\n if (timerId !== null) {\n clearTimeout(timerId);\n timerId = null;\n }\n pendingArgs = null;\n };\n\n return throttled;\n}\n","/**\n * Configuration options for the retry function.\n */\nexport interface RetryOptions {\n /**\n * Maximum number of retry attempts.\n */\n readonly attempts: number;\n\n /**\n * Delay between retries in milliseconds.\n */\n readonly delay: number;\n\n /**\n * Multiplier for exponential backoff (optional).\n * Each retry delay is multiplied by this factor.\n * @default 1\n */\n readonly backoff?: number;\n\n /**\n * Optional predicate to determine if an error should trigger a retry.\n * @param error - The error that occurred.\n * @returns `true` to retry, `false` to fail immediately.\n */\n readonly shouldRetry?: (error: unknown) => boolean;\n}\n\n/**\n * Retries an async function with configurable attempts, delay, and backoff.\n *\n * @template T - The return type of the async function.\n * @param fn - The async function to retry.\n * @param options - Retry configuration.\n * @returns The result of the successful call.\n * @throws The last error if all attempts fail.\n *\n * @example\n * // Basic retry with 3 attempts\n * const data = await retry(\n * () => fetch('/api/data').then(r => r.json()),\n * { attempts: 3, delay: 1000 }\n * );\n *\n * @example\n * // With exponential backoff\n * const result = await retry(\n * () => unreliableApiCall(),\n * { attempts: 5, delay: 100, backoff: 2 } // delays: 100, 200, 400, 800, 1600\n * );\n *\n * @example\n * // With conditional retry\n * const result = await retry(\n * () => apiCall(),\n * {\n * attempts: 3,\n * delay: 500,\n * shouldRetry: (err) => err instanceof NetworkError\n * }\n * );\n */\nexport async function retry<T>(fn: () => Promise<T>, options: RetryOptions): Promise<T> {\n const { attempts, delay, backoff = 1, shouldRetry } = options;\n\n let lastError: unknown;\n let currentDelay = delay;\n\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n if (shouldRetry && !shouldRetry(error)) {\n throw error;\n }\n\n if (attempt < attempts) {\n await new Promise(resolve => setTimeout(resolve, currentDelay));\n currentDelay *= backoff;\n }\n }\n }\n\n throw lastError;\n}\n","/**\n * Creates a new object with only the specified keys from the source object.\n *\n * @template T - The type of the source object.\n * @template K - The keys to pick.\n * @param obj - The source object.\n * @param keys - The keys to pick from the object.\n * @returns A new object containing only the specified keys.\n *\n * @example\n * const user = { id: 1, name: 'John', email: 'john@example.com', age: 30 };\n * pick(user, ['id', 'name']); // { id: 1, name: 'John' }\n *\n * @example\n * const config = { host: 'localhost', port: 3000, debug: true };\n * pick(config, ['host', 'port']); // { host: 'localhost', port: 3000 }\n */\nexport function pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {\n const result = {} as Pick<T, K>;\n for (const key of keys) {\n if (key in obj) {\n result[key] = obj[key];\n }\n }\n return result;\n}\n","/**\n * Creates a new object with the specified keys omitted from the source object.\n *\n * @template T - The type of the source object.\n * @template K - The keys to omit.\n * @param obj - The source object.\n * @param keys - The keys to omit from the object.\n * @returns A new object without the specified keys.\n *\n * @example\n * const user = { id: 1, name: 'John', password: 'secret', email: 'john@example.com' };\n * omit(user, ['password']); // { id: 1, name: 'John', email: 'john@example.com' }\n *\n * @example\n * const config = { host: 'localhost', port: 3000, debug: true };\n * omit(config, ['debug']); // { host: 'localhost', port: 3000 }\n */\nexport function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {\n const result = { ...obj };\n for (const key of keys) {\n delete result[key];\n }\n return result as Omit<T, K>;\n}\n","/**\n * Creates a deep clone of a value, handling objects, arrays, Date, and RegExp.\n * Note: Does not handle circular references, functions, or special objects like Map, Set.\n * For complex cloning needs, consider using `structuredClone()`.\n *\n * @template T - The type of the value to clone.\n * @param value - The value to deep clone.\n * @returns A deep clone of the value.\n *\n * @example\n * const original = { a: 1, b: { c: 2, d: [3, 4] } };\n * const cloned = deepClone(original);\n * cloned.b.c = 5;\n * console.log(original.b.c); // 2 (unchanged)\n *\n * @example\n * const arr = [{ id: 1 }, { id: 2 }];\n * const clonedArr = deepClone(arr);\n * clonedArr[0].id = 99;\n * console.log(arr[0].id); // 1 (unchanged)\n */\nexport function deepClone<T>(value: T): T {\n if (value === null || typeof value !== 'object') {\n return value;\n }\n\n if (value instanceof Date) {\n return new Date(value.getTime()) as T;\n }\n\n if (value instanceof RegExp) {\n return new RegExp(value.source, value.flags) as T;\n }\n\n if (Array.isArray(value)) {\n return value.map(item => deepClone(item)) as T;\n }\n\n // At this point, value is guaranteed to be a plain object\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value)) {\n result[key] = deepClone((value as Record<string, unknown>)[key]);\n }\n return result as T;\n}\n","/**\n * Checks if a value is a plain object (not null, not an array).\n *\n * @param value - The value to check.\n * @returns `true` if value is a plain object; otherwise, `false`.\n *\n * @example\n * isPlainObject({ a: 1 }); // true\n * isPlainObject([1, 2]); // false\n * isPlainObject(null); // false\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import { deepClone } from './deep-clone';\nimport { isPlainObject } from './is-plain-object';\n\n/**\n * Deep merges multiple source objects into a new object.\n * Later sources override earlier ones. Arrays are replaced, not merged.\n *\n * @template T - The type of the resulting object.\n * @param sources - The source objects to merge.\n * @returns A new deeply merged object.\n *\n * @example\n * const defaults = { theme: { color: 'blue', size: 'md' }, debug: false };\n * const userConfig = { theme: { color: 'red' }, debug: true };\n * deepMerge(defaults, userConfig);\n * // { theme: { color: 'red', size: 'md' }, debug: true }\n *\n * @example\n * const a = { arr: [1, 2], nested: { x: 1 } };\n * const b = { arr: [3, 4], nested: { y: 2 } };\n * deepMerge(a, b);\n * // { arr: [3, 4], nested: { x: 1, y: 2 } } - arrays are replaced\n */\nexport function deepMerge<T extends object>(...sources: Partial<T>[]): T {\n const result: Record<string, unknown> = {};\n\n for (const source of sources) {\n if (typeof source !== 'object' || source === null) {\n continue;\n }\n\n for (const key of Object.keys(source)) {\n const sourceValue = (source as Record<string, unknown>)[key];\n const resultValue = result[key];\n\n if (Array.isArray(sourceValue)) {\n result[key] = deepClone(sourceValue);\n } else if (isPlainObject(sourceValue) && isPlainObject(resultValue)) {\n result[key] = deepMerge(\n resultValue as Record<string, unknown>,\n sourceValue as Record<string, unknown>,\n );\n } else if (isPlainObject(sourceValue)) {\n result[key] = deepClone(sourceValue);\n } else {\n result[key] = sourceValue;\n }\n }\n }\n\n return result as T;\n}\n","import { isPlainObject } from './is-plain-object';\n\n/**\n * Performs a deep equality comparison between two values.\n * Handles primitives, objects, arrays, Date, and RegExp.\n *\n * @template T - The type of values to compare.\n * @param a - The first value.\n * @param b - The second value.\n * @returns `true` if values are deeply equal; otherwise, `false`.\n *\n * @example\n * deepEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } }); // true\n * deepEqual({ a: 1 }, { a: 2 }); // false\n * deepEqual([1, [2, 3]], [1, [2, 3]]); // true\n * deepEqual([1, 2], [1, 2, 3]); // false\n * deepEqual(new Date('2024-01-01'), new Date('2024-01-01')); // true\n */\nexport function deepEqual<T>(a: T, b: T): boolean {\n if (a === b) {\n return true;\n }\n\n if (a === null || b === null) {\n return a === b;\n }\n\n if (typeof a !== typeof b) {\n return false;\n }\n\n if (typeof a !== 'object') {\n return a === b;\n }\n\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime();\n }\n\n if (a instanceof RegExp && b instanceof RegExp) {\n return a.source === b.source && a.flags === b.flags;\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) {\n return false;\n }\n }\n return true;\n }\n\n if (Array.isArray(a) !== Array.isArray(b)) {\n return false;\n }\n\n if (isPlainObject(a) && isPlainObject(b)) {\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) {\n return false;\n }\n\n for (const key of keysA) {\n if (!Object.prototype.hasOwnProperty.call(b, key)) {\n return false;\n }\n if (!deepEqual(a[key], b[key])) {\n return false;\n }\n }\n return true;\n }\n\n return false;\n}\n","/**\n * Utility type that recursively maps the object type to the type of the resolved path.\n *\n * @example\n * type A = { user: { profile: { name: string } } };\n * type Result = ResolvePath<A, 'user.profile.name'>; // Result is string\n */\nexport type ResolvePath<T, P extends string> = P extends `${infer Head}.${infer Tail}`\n ? Head extends keyof T\n ? ResolvePath<T[Head], Tail>\n : never\n : P extends keyof T\n ? T[P]\n : never;\n\n/**\n * Resolves a nested path in an object, returning the value at the specified path.\n *\n * This function splits the provided `path` into keys and iterates through the object to access the\n * corresponding value at each key. If any part of the path is undefined or if the path leads to a non-object,\n * it returns `undefined`.\n *\n * @template T - The type of the object.\n * @template P - The dot-separated string path type.\n * @param obj - The object to resolve the path within.\n * @param path - The dot-separated string representing the path to the value.\n * @returns The value at the resolved path, or `undefined` if any part of the path is invalid.\n *\n * @example\n * const obj = { user: { profile: { name: 'John Doe' } } };\n * const result = resolvePath(obj, 'user.profile.name');\n * console.log(result); // 'John Doe'\n *\n * @example\n * const result2 = resolvePath(obj, 'user.address.city');\n * console.log(result2); // undefined\n */\nexport function resolvePath<T extends object, P extends string>(\n obj: T,\n path: P,\n): ResolvePath<T, P> | undefined {\n const keys = path.split('.') as (keyof T)[];\n let current: unknown = obj;\n\n for (const key of keys) {\n if (current && typeof current === 'object' && key in current) {\n current = (current as Record<string | number | symbol, unknown>)[key];\n } else {\n return undefined;\n }\n }\n\n return current as ResolvePath<T, P>;\n}\n","/**\n * Splits an array into chunks of the specified size.\n *\n * @template T - The type of array elements.\n * @param array - The array to split.\n * @param size - The size of each chunk (must be positive).\n * @returns An array of chunks.\n *\n * @example\n * chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]\n * chunk([1, 2, 3, 4], 2); // [[1, 2], [3, 4]]\n * chunk([1, 2, 3], 5); // [[1, 2, 3]]\n * chunk([], 2); // []\n */\nexport function chunk<T>(array: T[], size: number): T[][] {\n if (size <= 0) {\n throw new Error('Chunk size must be a positive number');\n }\n\n const result: T[][] = [];\n for (let i = 0; i < array.length; i += size) {\n result.push(array.slice(i, i + size));\n }\n return result;\n}\n","/**\n * Splits an array into two groups based on a predicate function.\n * The first array contains elements that satisfy the predicate,\n * the second contains elements that don't.\n *\n * @template T - The type of array elements.\n * @param array - The array to partition.\n * @param predicate - The function to test each element.\n * @returns A tuple of [truthy, falsy] arrays.\n *\n * @example\n * const numbers = [1, 2, 3, 4, 5, 6];\n * partition(numbers, n => n % 2 === 0); // [[2, 4, 6], [1, 3, 5]]\n *\n * @example\n * const users = [\n * { name: 'Alice', active: true },\n * { name: 'Bob', active: false },\n * { name: 'Charlie', active: true }\n * ];\n * partition(users, u => u.active);\n * // [[{ name: 'Alice', active: true }, { name: 'Charlie', active: true }], [{ name: 'Bob', active: false }]]\n */\nexport function partition<T>(array: T[], predicate: (item: T) => boolean): [T[], T[]] {\n const truthy: T[] = [];\n const falsy: T[] = [];\n\n for (const item of array) {\n if (predicate(item)) {\n truthy.push(item);\n } else {\n falsy.push(item);\n }\n }\n\n return [truthy, falsy];\n}\n","/**\n * Groups an array of items based on a provided predicate function.\n *\n * @template K - The type of keys produced by the predicate, typically a string, number, or symbol.\n * @template V - The type of items in the collection.\n * @param collection - The array of items to be grouped.\n * @param predicate - A function that takes an item and returns a key by which to group the item.\n * @returns An object where each key is a group identifier produced by the predicate,\n * and the value is an array of items belonging to that group.\n *\n * @example\n * // Group an array of people by their age\n * const people = [{ name: 'Alice', age: 25 }, { name: 'Bob', age: 25 }, { name: 'Charlie', age: 30 }];\n * const groupedByAge = groupBy(people, person => person.age);\n * // Output:\n * // {\n * // 25: [{ name: 'Alice', age: 25 }, { name: 'Bob', age: 25 }],\n * // 30: [{ name: 'Charlie', age: 30 }]\n * // }\n */\nexport function groupBy<K extends string | number | symbol, V>(\n collection: V[],\n predicate: (item: V) => K,\n): Record<K, V[]> {\n return collection.reduce(\n (groups, item) => {\n const groupKey = predicate(item);\n if (!groups[groupKey]) {\n groups[groupKey] = [];\n }\n groups[groupKey].push(item);\n return groups;\n },\n {} as Record<K, V[]>,\n );\n}\n","/**\n * ngx-com/utils\n * Utility function exports for ngx-com library\n */\n\nexport { mergeClasses } from './merge-classes';\n\n// Function utilities\nexport { debounce, type DebouncedFn, type DebounceOptions } from './debounce';\nexport { throttle, type ThrottledFn } from './throttle';\nexport { retry, type RetryOptions } from './retry';\n\n// Object utilities\nexport { pick } from './pick';\nexport { omit } from './omit';\nexport { deepClone } from './deep-clone';\nexport { deepMerge } from './deep-merge';\nexport { deepEqual } from './deep-equal';\nexport { isPlainObject } from './is-plain-object';\nexport { resolvePath, type ResolvePath } from './resolve-path';\n\n// Array utilities\nexport { chunk } from './chunk';\nexport { partition } from './partition';\nexport { groupBy } from './group-by';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}