• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 16193550997

10 Jul 2025 11:12AM UTC coverage: 4.657% (-87.0%) from 91.64%
16193550997

Pull #16028

github

web-flow
Merge f7a9963b8 into 87246e3ce
Pull Request #16028: fix(radio-group): dynamically added radio buttons do not initialize

178 of 15764 branches covered (1.13%)

18 of 19 new or added lines in 2 files covered. (94.74%)

25721 existing lines in 324 files now uncovered.

1377 of 29570 relevant lines covered (4.66%)

0.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

16.34
/projects/igniteui-angular/src/lib/core/utils.ts
1
import { CurrencyPipe, formatDate as _formatDate, isPlatformBrowser } from '@angular/common';
2
import { Inject, Injectable, InjectionToken, PLATFORM_ID, inject } from '@angular/core';
3
import { mergeWith } from 'lodash-es';
4
import { NEVER, Observable } from 'rxjs';
5
import { setImmediate } from './setImmediate';
6
import { isDevMode } from '@angular/core';
7
import type { IgxTheme } from '../services/theme/theme.token';
8

9
/** @hidden @internal */
10
export const ELEMENTS_TOKEN = /*@__PURE__*/new InjectionToken<boolean>('elements environment');
3✔
11

12
/**
13
 * @hidden
14
 */
15
export const showMessage = (message: string, isMessageShown: boolean): boolean => {
3✔
16
    if (!isMessageShown && isDevMode()) {
×
17
        console.warn(message);
×
18
    }
19

20
    return true;
×
21
};
22

23
export const mkenum = <T extends { [index: string]: U }, U extends string>(x: T) => x;
111✔
24

25
/**
26
 *
27
 * @hidden @internal
28
 */
29
export const getResizeObserver = () => globalThis.window?.ResizeObserver;
3✔
30

31
/**
32
 * @hidden
33
 */
34
export function cloneArray<T>(array: T[], deep = false): T[] {
×
UNCOV
35
    return deep ? (array ?? []).map(cloneValue) : (array ?? []).slice();
×
36
}
37

38
/**
39
 * Doesn't clone leaf items
40
 *
41
 * @hidden
42
 */
43
export const cloneHierarchicalArray = (array: any[], childDataKey: any): any[] => {
3✔
UNCOV
44
    const result: any[] = [];
×
UNCOV
45
    if (!array) {
×
46
        return result;
×
47
    }
48

UNCOV
49
    for (const item of array) {
×
UNCOV
50
        const clonedItem = cloneValue(item);
×
UNCOV
51
        if (Array.isArray(item[childDataKey])) {
×
UNCOV
52
            clonedItem[childDataKey] = cloneHierarchicalArray(clonedItem[childDataKey], childDataKey);
×
53
        }
UNCOV
54
        result.push(clonedItem);
×
55
    }
UNCOV
56
    return result;
×
57
};
58

59
/**
60
 * Creates an object with prototype from provided source and copies
61
 * all properties descriptors from provided source
62
 * @param obj Source to copy prototype and descriptors from
63
 * @returns New object with cloned prototype and property descriptors
64
 */
65
export const copyDescriptors = (obj) => {
3✔
UNCOV
66
    if (obj) {
×
UNCOV
67
        return Object.create(
×
68
            Object.getPrototypeOf(obj),
69
            Object.getOwnPropertyDescriptors(obj)
70
        );
71
    }
72
}
73

74

75
/**
76
 * Deep clones all first level keys of Obj2 and merges them to Obj1
77
 *
78
 * @param obj1 Object to merge into
79
 * @param obj2 Object to merge from
80
 * @returns Obj1 with merged cloned keys from Obj2
81
 * @hidden
82
 */
83
export const mergeObjects = (obj1: any, obj2: any): any => mergeWith(obj1, obj2, (objValue, srcValue) => {
3✔
UNCOV
84
    if (Array.isArray(srcValue)) {
×
UNCOV
85
        return objValue = srcValue;
×
86
    }
87
});
88

89
/**
90
 * Creates deep clone of provided value.
91
 * Supports primitive values, dates and objects.
92
 * If passed value is array returns shallow copy of the array.
93
 *
94
 * @param value value to clone
95
 * @returns Deep copy of provided value
96
 * @hidden
97
 */
98
export const cloneValue = (value: any): any => {
3✔
UNCOV
99
    if (isDate(value)) {
×
UNCOV
100
        return new Date(value.getTime());
×
101
    }
UNCOV
102
    if (Array.isArray(value)) {
×
UNCOV
103
        return value.slice();
×
104
    }
105

UNCOV
106
    if (value instanceof Map || value instanceof Set) {
×
UNCOV
107
        return value;
×
108
    }
109

UNCOV
110
    if (isObject(value)) {
×
UNCOV
111
        const result = {};
×
112

UNCOV
113
        for (const key of Object.keys(value)) {
×
UNCOV
114
            if (key === "externalObject") {
×
115
                continue;
×
116
            }
UNCOV
117
            result[key] = cloneValue(value[key]);
×
118
        }
UNCOV
119
        return result;
×
120
    }
UNCOV
121
    return value;
×
122
};
123

124
/**
125
 * Creates deep clone of provided value.
126
 * Supports primitive values, dates and objects.
127
 * If passed value is array returns shallow copy of the array.
128
 * For Objects property values and references are cached and reused.
129
 * This allows for circular references to same objects.
130
 *
131
 * @param value value to clone
132
 * @param cache map of cached values already parsed
133
 * @returns Deep copy of provided value
134
 * @hidden
135
 */
136
export const cloneValueCached = (value: any, cache: Map<any, any>): any => {
3✔
137
    if (isDate(value)) {
×
138
        return new Date(value.getTime());
×
139
    }
140
    if (Array.isArray(value)) {
×
141
        return [...value];
×
142
    }
143

144
    if (value instanceof Map || value instanceof Set) {
×
145
        return value;
×
146
    }
147

148
    if (isObject(value)) {
×
149
        if (cache.has(value)) {
×
150
            return cache.get(value);
×
151
        }
152

153
        const result = {};
×
154
        cache.set(value, result);
×
155

156
        for (const key of Object.keys(value)) {
×
157
            result[key] = cloneValueCached(value[key], cache);
×
158
        }
159
        return result;
×
160
    }
161
    return value;
×
162
};
163

164
/**
165
 * Parse provided input to Date.
166
 *
167
 * @param value input to parse
168
 * @returns Date if parse succeed or null
169
 * @hidden
170
 */
171
export const parseDate = (value: any): Date | null => {
3✔
172
    // if value is Invalid Date return null
UNCOV
173
    if (isDate(value)) {
×
UNCOV
174
        return !isNaN(value.getTime()) ? value : null;
×
175
    }
UNCOV
176
    return value ? new Date(value) : null;
×
177
};
178

179
/**
180
 * Returns an array with unique dates only.
181
 *
182
 * @param columnValues collection of date values (might be numbers or ISO 8601 strings)
183
 * @returns collection of unique dates.
184
 * @hidden
185
 */
186
export const uniqueDates = (columnValues: any[]) => columnValues.reduce((a, c) => {
3✔
187
    if (!a.cache[c.label]) {
×
188
        a.result.push(c);
×
189
    }
190
    a.cache[c.label] = true;
×
191
    return a;
×
192
}, { result: [], cache: {} }).result;
193

194
/**
195
 * Checks if provided variable is Object
196
 *
197
 * @param value Value to check
198
 * @returns true if provided variable is Object
199
 * @hidden
200
 */
201
export const isObject = (value: any): boolean => !!(value && value.toString() === '[object Object]');
3!
202

203
/**
204
 * Checks if provided variable is Date
205
 *
206
 * @param value Value to check
207
 * @returns true if provided variable is Date
208
 * @hidden
209
 */
210
export const isDate = (value: any): value is Date => {
3✔
UNCOV
211
    return Object.prototype.toString.call(value) === "[object Date]";
×
212
}
213

214
/**
215
 * Checks if the two passed arguments are equal
216
 * Currently supports date objects
217
 *
218
 * @param obj1
219
 * @param obj2
220
 * @returns: `boolean`
221
 * @hidden
222
 */
223
export const isEqual = (obj1, obj2): boolean => {
3✔
UNCOV
224
    if (isDate(obj1) && isDate(obj2)) {
×
UNCOV
225
        return obj1.getTime() === obj2.getTime();
×
226
    }
UNCOV
227
    return obj1 === obj2;
×
228
};
229

230
/**
231
 * Utility service taking care of various utility functions such as
232
 * detecting browser features, general cross browser DOM manipulation, etc.
233
 *
234
 * @hidden @internal
235
 */
236
@Injectable({ providedIn: 'root' })
237
export class PlatformUtil {
3✔
UNCOV
238
    public isBrowser: boolean = isPlatformBrowser(this.platformId);
×
UNCOV
239
    public isIOS = this.isBrowser && /iPad|iPhone|iPod/.test(navigator.userAgent) && !('MSStream' in window);
×
UNCOV
240
    public isSafari = this.isBrowser && /Safari[\/\s](\d+\.\d+)/.test(navigator.userAgent);
×
UNCOV
241
    public isFirefox = this.isBrowser && /Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent);
×
UNCOV
242
    public isEdge = this.isBrowser && /Edge[\/\s](\d+\.\d+)/.test(navigator.userAgent);
×
UNCOV
243
    public isChromium = this.isBrowser && (/Chrom|e?ium/g.test(navigator.userAgent) ||
×
244
        /Google Inc/g.test(navigator.vendor)) && !/Edge/g.test(navigator.userAgent);
UNCOV
245
    public browserVersion = this.isBrowser ? parseFloat(navigator.userAgent.match(/Version\/([\d.]+)/)?.at(1)) : 0;
×
246

247
    /** @hidden @internal */
UNCOV
248
    public isElements = inject(ELEMENTS_TOKEN, { optional: true });
×
249

UNCOV
250
    public KEYMAP = mkenum({
×
251
        ENTER: 'Enter',
252
        SPACE: ' ',
253
        ESCAPE: 'Escape',
254
        ARROW_DOWN: 'ArrowDown',
255
        ARROW_UP: 'ArrowUp',
256
        ARROW_LEFT: 'ArrowLeft',
257
        ARROW_RIGHT: 'ArrowRight',
258
        END: 'End',
259
        HOME: 'Home',
260
        PAGE_DOWN: 'PageDown',
261
        PAGE_UP: 'PageUp',
262
        F2: 'F2',
263
        TAB: 'Tab',
264
        SEMICOLON: ';',
265
        // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values#editing_keys
266
        DELETE: 'Delete',
267
        BACKSPACE: 'Backspace',
268
        CONTROL: 'Control',
269
        X: 'x',
270
        Y: 'y',
271
        Z: 'z'
272
    });
273

UNCOV
274
    constructor(@Inject(PLATFORM_ID) private platformId: any) { }
×
275

276
    /**
277
     * @hidden @internal
278
     * Returns the actual size of the node content, using Range
279
     * ```typescript
280
     * let range = document.createRange();
281
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
282
     *
283
     * let size = getNodeSizeViaRange(range, column.cells[0].nativeElement);
284
     *
285
     * @remarks
286
     * The last parameter is useful when the size of the element to measure is modified by a
287
     * parent element that has explicit size. In such cases the calculated size is never lower
288
     * and the function may instead remove the parent size while measuring to get the correct value.
289
     * ```
290
     */
291
    public getNodeSizeViaRange(range: Range, node: HTMLElement, sizeHoldingNode?: HTMLElement) {
UNCOV
292
        let overflow = null;
×
293
        let nodeStyles: string[];
294

UNCOV
295
        if (!this.isFirefox) {
×
UNCOV
296
            overflow = node.style.overflow;
×
297
            // we need that hack - otherwise content won't be measured correctly in IE/Edge
UNCOV
298
            node.style.overflow = 'visible';
×
299
        }
300

UNCOV
301
        if (sizeHoldingNode) {
×
UNCOV
302
            const style = sizeHoldingNode.style;
×
UNCOV
303
            nodeStyles = [style.width, style.minWidth, style.flexBasis];
×
UNCOV
304
            style.width = '';
×
UNCOV
305
            style.minWidth = '';
×
UNCOV
306
            style.flexBasis = '';
×
307
        }
308

UNCOV
309
        range.selectNodeContents(node);
×
UNCOV
310
        const scale = node.getBoundingClientRect().width / node.offsetWidth;
×
UNCOV
311
        const width = range.getBoundingClientRect().width / scale;
×
312

UNCOV
313
        if (!this.isFirefox) {
×
314
            // we need that hack - otherwise content won't be measured correctly in IE/Edge
UNCOV
315
            node.style.overflow = overflow;
×
316
        }
317

UNCOV
318
        if (sizeHoldingNode) {
×
UNCOV
319
            sizeHoldingNode.style.width = nodeStyles[0];
×
UNCOV
320
            sizeHoldingNode.style.minWidth = nodeStyles[1];
×
UNCOV
321
            sizeHoldingNode.style.flexBasis = nodeStyles[2];
×
322
        }
323

UNCOV
324
        return width;
×
325
    }
326

327

328
    /**
329
     * Returns true if the current keyboard event is an activation key (Enter/Space bar)
330
     *
331
     * @hidden
332
     * @internal
333
     *
334
     * @memberof PlatformUtil
335
     */
336
    public isActivationKey(event: KeyboardEvent) {
UNCOV
337
        return event.key === this.KEYMAP.ENTER || event.key === this.KEYMAP.SPACE;
×
338
    }
339

340
    /**
341
     * Returns true if the current keyboard event is a combination that closes the filtering UI of the grid. (Escape/Ctrl+Shift+L)
342
     *
343
     * @hidden
344
     * @internal
345
     * @param event
346
     * @memberof PlatformUtil
347
     */
348
    public isFilteringKeyCombo(event: KeyboardEvent) {
UNCOV
349
        return event.key === this.KEYMAP.ESCAPE || (event.ctrlKey && event.shiftKey && event.key.toLowerCase() === 'l');
×
350
    }
351

352
    /**
353
     * @hidden @internal
354
     */
355
    public isLeftClick(event: PointerEvent | MouseEvent) {
UNCOV
356
        return event.button === 0;
×
357
    }
358

359
    /**
360
     * @hidden @internal
361
     */
362
    public isNavigationKey(key: string) {
UNCOV
363
        return [
×
364
            this.KEYMAP.HOME, this.KEYMAP.END, this.KEYMAP.SPACE,
365
            this.KEYMAP.ARROW_DOWN, this.KEYMAP.ARROW_LEFT, this.KEYMAP.ARROW_RIGHT, this.KEYMAP.ARROW_UP
366
        ].includes(key as any);
367
    }
368
}
369

370
/**
371
 * @hidden
372
 */
373
export const flatten = (arr: any[]) => {
3✔
UNCOV
374
    let result = [];
×
375

UNCOV
376
    arr.forEach(el => {
×
UNCOV
377
        result.push(el);
×
UNCOV
378
        if (el.children) {
×
UNCOV
379
            const children = Array.isArray(el.children) ? el.children : el.children.toArray();
×
UNCOV
380
            result = result.concat(flatten(children));
×
381
        }
382
    });
UNCOV
383
    return result;
×
384
};
385

386
export interface CancelableEventArgs {
387
    /**
388
     * Provides the ability to cancel the event.
389
     */
390
    cancel: boolean;
391
}
392

393
export interface IBaseEventArgs {
394
    /**
395
     * Provides reference to the owner component.
396
     */
397
    owner?: any;
398
}
399

400
export interface CancelableBrowserEventArgs extends CancelableEventArgs {
401
    /* blazorSuppress */
402
    /** Browser event */
403
    event?: Event;
404
}
405

406
export interface IBaseCancelableBrowserEventArgs extends CancelableBrowserEventArgs, IBaseEventArgs { }
407

408
export interface IBaseCancelableEventArgs extends CancelableEventArgs, IBaseEventArgs { }
409

410
export const HORIZONTAL_NAV_KEYS = new Set(['arrowleft', 'left', 'arrowright', 'right', 'home', 'end']);
3✔
411

412
export const NAVIGATION_KEYS = new Set([
3✔
413
    'down',
414
    'up',
415
    'left',
416
    'right',
417
    'arrowdown',
418
    'arrowup',
419
    'arrowleft',
420
    'arrowright',
421
    'home',
422
    'end',
423
    'space',
424
    'spacebar',
425
    ' '
426
]);
427
export const ACCORDION_NAVIGATION_KEYS = new Set('up down arrowup arrowdown home end'.split(' '));
3✔
428
export const ROW_EXPAND_KEYS = new Set('right down arrowright arrowdown'.split(' '));
3✔
429
export const ROW_COLLAPSE_KEYS = new Set('left up arrowleft arrowup'.split(' '));
3✔
430
export const ROW_ADD_KEYS = new Set(['+', 'add', '≠', '±', '=']);
3✔
431
export const SUPPORTED_KEYS = new Set([...Array.from(NAVIGATION_KEYS),
3✔
432
...Array.from(ROW_ADD_KEYS), 'enter', 'f2', 'escape', 'esc', 'pagedown', 'pageup']);
433
export const HEADER_KEYS = new Set([...Array.from(NAVIGATION_KEYS), 'escape', 'esc', 'l',
3✔
434
    /** This symbol corresponds to the Alt + L combination under MAC. */
435
    '¬']);
436

437
/**
438
 * @hidden
439
 * @internal
440
 *
441
 * Creates a new ResizeObserver on `target` and returns it as an Observable.
442
 * Run the resizeObservable outside angular zone, because it patches the MutationObserver which causes an infinite loop.
443
 * Related issue: https://github.com/angular/angular/issues/31712
444
 */
445
export const resizeObservable = (target: HTMLElement): Observable<ResizeObserverEntry[]> => {
3✔
UNCOV
446
    const resizeObserver = getResizeObserver();
×
447
    // check whether we are on server env or client env
UNCOV
448
    if (resizeObserver) {
×
UNCOV
449
        return new Observable((observer) => {
×
UNCOV
450
            const instance = new resizeObserver((entries: ResizeObserverEntry[]) => {
×
UNCOV
451
                observer.next(entries);
×
452
            });
UNCOV
453
            instance.observe(target);
×
UNCOV
454
            const unsubscribe = () => instance.disconnect();
×
UNCOV
455
            return unsubscribe;
×
456
        });
457
    }
458
    // if on a server env return a empty observable that does not complete immediately
459
    return NEVER;
×
460

461
}
462

463
/**
464
 * @hidden
465
 * @internal
466
 *
467
 * Compares two maps.
468
 */
469
export const compareMaps = (map1: Map<any, any>, map2: Map<any, any>): boolean => {
3✔
UNCOV
470
    if (!map2) {
×
UNCOV
471
        return !map1;
×
472
    }
UNCOV
473
    if (map1.size !== map2.size) {
×
474
        return false;
×
475
    }
UNCOV
476
    let match = true;
×
UNCOV
477
    const keys = Array.from(map2.keys());
×
UNCOV
478
    for (const key of keys) {
×
UNCOV
479
        if (map1.has(key)) {
×
UNCOV
480
            match = map1.get(key) === map2.get(key);
×
481
        } else {
482
            match = false;
×
483
        }
UNCOV
484
        if (!match) {
×
UNCOV
485
            break;
×
486
        }
487
    }
UNCOV
488
    return match;
×
489
};
490

491
function _isObject(entity: unknown): entity is object {
UNCOV
492
    return entity != null && typeof entity === 'object';
×
493
}
494

495
export function columnFieldPath(path?: string): string[] {
UNCOV
496
    return path?.split('.') ?? [];
×
497
}
498

499
/**
500
 * Given a property access path in the format `x.y.z` resolves and returns
501
 * the value of the `z` property in the passed object.
502
 *
503
 * @hidden
504
 * @internal
505
 */
506
export function resolveNestedPath<T extends object, U>(obj: unknown, pathParts: string[], defaultValue?: U): T | U | undefined {
UNCOV
507
    if (!_isObject(obj) || pathParts.length < 1) {
×
UNCOV
508
        return defaultValue;
×
509
    }
510

UNCOV
511
    let current = obj;
×
512

UNCOV
513
    for (const key of pathParts) {
×
UNCOV
514
        if (_isObject(current) && key in (current as T)) {
×
UNCOV
515
            current = current[key];
×
516
        } else {
UNCOV
517
            return defaultValue;
×
518
        }
519
    }
520

UNCOV
521
    return current as T;
×
522
}
523

524
/**
525
 *
526
 * Given a property access path in the format `x.y.z` and a value
527
 * this functions builds and returns an object following the access path.
528
 *
529
 * @example
530
 * ```typescript
531
 * console.log('x.y.z.', 42);
532
 * >> { x: { y: { z: 42 } } }
533
 * ```
534
 *
535
 * @hidden
536
 * @internal
537
 */
538
export const reverseMapper = (path: string, value: any) => {
3✔
UNCOV
539
    const obj = {};
×
UNCOV
540
    const parts = path?.split('.') ?? [];
×
541

UNCOV
542
    let _prop = parts.shift();
×
543
    let mapping: any;
544

545
    // Initial binding for first level bindings
UNCOV
546
    obj[_prop] = value;
×
UNCOV
547
    mapping = obj;
×
548

UNCOV
549
    parts.forEach(prop => {
×
550
        // Start building the hierarchy
UNCOV
551
        mapping[_prop] = {};
×
552
        // Go down a level
UNCOV
553
        mapping = mapping[_prop];
×
554
        // Bind the value and move the key
UNCOV
555
        mapping[prop] = value;
×
UNCOV
556
        _prop = prop;
×
557
    });
558

UNCOV
559
    return obj;
×
560
};
561

562
export const yieldingLoop = (count: number, chunkSize: number, callback: (index: number) => void, done: () => void) => {
3✔
UNCOV
563
    let i = 0;
×
UNCOV
564
    const chunk = () => {
×
UNCOV
565
        const end = Math.min(i + chunkSize, count);
×
UNCOV
566
        for (; i < end; ++i) {
×
UNCOV
567
            callback(i);
×
568
        }
UNCOV
569
        if (i < count) {
×
UNCOV
570
            setImmediate(chunk);
×
571
        } else {
UNCOV
572
            done();
×
573
        }
574
    };
UNCOV
575
    chunk();
×
576
};
577

578
export const isConstructor = (ref: any) => typeof ref === 'function' && Boolean(ref.prototype) && Boolean(ref.prototype.constructor);
3!
579

580
/**
581
 * Similar to Angular's formatDate. However it will not throw on `undefined | null | ''` instead
582
 * coalescing to an empty string.
583
 */
584
export const formatDate = (value: string | number | Date, format: string, locale: string, timezone?: string): string => {
3✔
UNCOV
585
    if (value === null || value === undefined || value === '') {
×
UNCOV
586
        return '';
×
587
    }
UNCOV
588
    return _formatDate(value, format, locale, timezone);
×
589
};
590

591
export const formatCurrency = new CurrencyPipe(undefined).transform;
3✔
592

593
/** Converts pixel values to their rem counterparts for a base value */
594
export const rem = (value: number | string) => {
3✔
UNCOV
595
    const base = parseFloat(globalThis.window?.getComputedStyle(globalThis.document?.documentElement).getPropertyValue('--ig-base-font-size'))
×
UNCOV
596
    return Number(value) / base;
×
597
}
598

599
/** Get the size of the component as derived from the CSS size variable */
600
export function getComponentSize(el: Element) {
UNCOV
601
    return globalThis.window?.getComputedStyle(el).getPropertyValue('--component-size');
×
602
}
603

604
/** Get the first item in an array */
605
export function first<T>(arr: T[]) {
UNCOV
606
    return arr.at(0) as T;
×
607
}
608

609
/** Get the last item in an array */
610
export function last<T>(arr: T[]) {
UNCOV
611
    return arr.at(-1) as T;
×
612
}
613

614
/** Calculates the modulo of two numbers, ensuring a non-negative result. */
615
export function modulo(n: number, d: number) {
UNCOV
616
    return ((n % d) + d) % d;
×
617
}
618

619
/**
620
 * Splits an array into chunks of length `size` and returns a generator
621
 * yielding each chunk.
622
 * The last chunk may contain less than `size` elements.
623
 *
624
 * @example
625
 * ```typescript
626
 * const arr = [0,1,2,3,4,5,6,7,8,9];
627
 *
628
 * Array.from(chunk(arr, 2)) // [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]
629
 * Array.from(chunk(arr, 3)) // [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
630
 * Array.from(chunk([], 3)) // []
631
 * Array.from(chunk(arr, -3)) // Error
632
 * ```
633
 */
634
export function* intoChunks<T>(arr: T[], size: number) {
UNCOV
635
    if (size < 1) {
×
636
        throw new Error('size must be an integer >= 1');
×
637
    }
UNCOV
638
    for (let i = 0; i < arr.length; i += size) {
×
UNCOV
639
        yield arr.slice(i, i + size);
×
640
    }
641
}
642

643
/**
644
 * @param size
645
 * @returns string that represents the --component-size default value
646
 */
647
export function getComponentCssSizeVar(size: string) {
648
    switch (size) {
×
649
        case "1":
650
            return 'var(--ig-size, var(--ig-size-small))';
×
651
        case "2":
652
            return 'var(--ig-size, var(--ig-size-medium))';
×
653
        default:
654
            return 'var(--ig-size, var(--ig-size-large))';
×
655
    }
656
}
657

658
/**
659
 * @param path - The URI path to be normalized.
660
 * @returns string encoded using the encodeURI function.
661
 */
662
export function normalizeURI(path: string) {
UNCOV
663
    return path?.split('/').map(encodeURI).join('/');
×
664
}
665

666
export function getComponentTheme(el: Element) {
667
    return globalThis.window
43✔
668
        ?.getComputedStyle(el)
669
        .getPropertyValue('--theme')
670
        .trim() as IgxTheme;
671
}
672

673
/**
674
 * Collection re-created w/ the built in track by identity will always log
675
 * warning even for valid cases of recalculating all collection items.
676
 * See https://github.com/angular/angular/blob/55581b4181639568fb496e91055142a1b489e988/packages/core/src/render3/instructions/control_flow.ts#L393-L409
677
 * Current solution explicit track function doing the same as suggested in:
678
 * https://github.com/angular/angular/issues/56471#issuecomment-2180315803
679
 * This should be used with moderation and when necessary.
680
 * @internal
681
 */
682
export function trackByIdentity<T>(item: T): T {
UNCOV
683
    return item;
×
684
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc