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

IgniteUI / igniteui-angular / 13566066243

27 Feb 2025 12:11PM CUT coverage: 91.648%. Remained the same
13566066243

Pull #15414

github

web-flow
Merge f1dc4ef55 into 76058930e
Pull Request #15414: feat(list): add selected property to the list-item component

13331 of 15598 branches covered (85.47%)

3 of 3 new or added lines in 1 file covered. (100.0%)

3 existing lines in 1 file now uncovered.

26886 of 29336 relevant lines covered (91.65%)

33702.07 hits per line

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

97.0
/projects/igniteui-angular/src/lib/list/list-item.component.ts
1
import {
2
    ChangeDetectionStrategy,
3
    Component,
4
    ElementRef,
5
    HostBinding,
6
    HostListener,
7
    Input,
8
    Renderer2,
9
    ViewChild,
10
    booleanAttribute
11
} from '@angular/core';
12

13
import {
14
    IgxListPanState,
15
    IListChild,
16
    IgxListBaseDirective
17
} from './list.common';
18

19
import { HammerGesturesManager } from '../core/touch';
20
import { rem } from '../core/utils';
21
import { NgTemplateOutlet } from '@angular/common';
22
import { Host } from 'igniteui-dockmanager/dist/types/stencil-public-runtime';
23

24
/**
25
 * The Ignite UI List Item component is a container intended for row items in the Ignite UI for Angular List component.
26
 *
27
 * Example:
28
 * ```html
29
 * <igx-list>
30
 *   <igx-list-item isHeader="true">Contacts</igx-list-item>
31
 *   <igx-list-item *ngFor="let contact of contacts">
32
 *     <span class="name">{{ contact.name }}</span>
33
 *     <span class="phone">{{ contact.phone }}</span>
34
 *   </igx-list-item>
35
 * </igx-list>
36
 * ```
37
 */
38
@Component({
39
    providers: [HammerGesturesManager],
40
    selector: 'igx-list-item',
41
    templateUrl: 'list-item.component.html',
42
    changeDetection: ChangeDetectionStrategy.OnPush,
43
    imports: [NgTemplateOutlet]
44
})
45
export class IgxListItemComponent implements IListChild {
2✔
46
    /**
47
     * Provides a reference to the template's base element shown when left panning a list item.
48
     * ```typescript
49
     * const leftPanTmpl = this.listItem.leftPanningTemplateElement;
50
     * ```
51
     */
52
    @ViewChild('leftPanningTmpl')
53
    public leftPanningTemplateElement;
54

55
    /**
56
     * Provides a reference to the template's base element shown when right panning a list item.
57
     * ```typescript
58
     * const rightPanTmpl = this.listItem.rightPanningTemplateElement;
59
     * ```
60
     */
61
    @ViewChild('rightPanningTmpl')
62
    public rightPanningTemplateElement;
63

64
    /**
65
     * Sets/gets whether the `list item` is a header.
66
     * ```html
67
     * <igx-list-item [isHeader] = "true">Header</igx-list-item>
68
     * ```
69
     * ```typescript
70
     * let isHeader =  this.listItem.isHeader;
71
     * ```
72
     *
73
     * @memberof IgxListItemComponent
74
     */
75
    @Input({ transform: booleanAttribute })
76
    public isHeader: boolean;
77

78
    /**
79
     * Sets/gets whether the `list item` is hidden.
80
     * By default the `hidden` value is `false`.
81
     * ```html
82
     * <igx-list-item [hidden] = "true">Hidden Item</igx-list-item>
83
     * ```
84
     * ```typescript
85
     * let isHidden =  this.listItem.hidden;
86
     * ```
87
     *
88
     * @memberof IgxListItemComponent
89
     */
90
    @Input({ transform: booleanAttribute })
91
    public hidden = false;
2,052✔
92

93
    /**
94
     * Sets/gets the `aria-label` attribute of the `list item`.
95
     * ```typescript
96
     * this.listItem.ariaLabel = "Item1";
97
     * ```
98
     * ```typescript
99
     * let itemAriaLabel = this.listItem.ariaLabel;
100
     * ```
101
     *
102
     * @memberof IgxListItemComponent
103
     */
104
    @HostBinding('attr.aria-label')
105
    public ariaLabel: string;
106

107
    /**
108
     * Gets the `touch-action` style of the `list item`.
109
     * ```typescript
110
     * let touchAction = this.listItem.touchAction;
111
     * ```
112
     */
113
    @HostBinding('style.touch-action')
114
    public touchAction = 'pan-y';
2,052✔
115

116
    /**
117
     * @hidden
118
     */
119
    private _panState: IgxListPanState = IgxListPanState.NONE;
2,052✔
120

121
    /**
122
     * @hidden
123
     */
124
    private panOffset = 0;
2,052✔
125

126
    /**
127
     * @hidden
128
     */
129
    private _index: number = null;
2,052✔
130

131
    /**
132
     * @hidden
133
     */
134
    private lastPanDir = IgxListPanState.NONE;
2,052✔
135

136
    private _role: string = '';
2,052✔
137
    private _selected = false;;
2,052✔
138

139
    /**
140
     * Gets the `panState` of a `list item`.
141
     * ```typescript
142
     * let itemPanState =  this.listItem.panState;
143
     * ```
144
     *
145
     * @memberof IgxListItemComponent
146
     */
147
    public get panState(): IgxListPanState {
148
        return this._panState;
12✔
149
    }
150

151
    /**
152
     * Gets the `index` of a `list item`.
153
     * ```typescript
154
     * let itemIndex =  this.listItem.index;
155
     * ```
156
     *
157
     * @memberof IgxListItemComponent
158
     */
159
    @Input()
160
    public get index(): number {
161
        return this._index !== null ? this._index : this.list.children.toArray().indexOf(this);
504✔
162
    }
163

164
    /**
165
     * Sets the `index` of the `list item`.
166
     * ```typescript
167
     * this.listItem.index = index;
168
     * ```
169
     *
170
     * @memberof IgxListItemComponent
171
     */
172
    public set index(value: number) {
173
        this._index = value;
18✔
174
    }
175

176
    /**
177
     * Returns an element reference to the list item.
178
     * ```typescript
179
     * let listItemElement =  this.listItem.element.
180
     * ```
181
     *
182
     * @memberof IgxListItemComponent
183
     */
184
    public get element() {
185
        return this.elementRef.nativeElement;
316✔
186
    }
187

188
    /**
189
     * Returns a reference container which contains the list item's content.
190
     * ```typescript
191
     * let listItemContainer =  this.listItem.contentElement.
192
     * ```
193
     *
194
     * @memberof IgxListItemComponent
195
     */
196
    public get contentElement() {
197
        const candidates = this.element.getElementsByClassName('igx-list__item-content');
33✔
198
        return (candidates && candidates.length > 0) ? candidates[0] : null;
33✔
199
    }
200

201
    /**
202
     * Returns the `context` object which represents the `template context` binding into the `list item container`
203
     * by providing the `$implicit` declaration which is the `IgxListItemComponent` itself.
204
     * ```typescript
205
     * let listItemComponent = this.listItem.context;
206
     * ```
207
     */
208
    public get context(): any {
209
        return {
72✔
210
            $implicit: this
211
        };
212
    }
213

214
    /**
215
     * Gets the width of a `list item`.
216
     * ```typescript
217
     * let itemWidth = this.listItem.width;
218
     * ```
219
     *
220
     * @memberof IgxListItemComponent
221
     */
222
    public get width() {
223
        if (this.element) {
41✔
224
            return this.element.offsetWidth;
41✔
225
        }
226
    }
227

228
    /**
229
     * Gets the maximum left position of the `list item`.
230
     * ```typescript
231
     * let maxLeft = this.listItem.maxLeft;
232
     * ```
233
     *
234
     * @memberof IgxListItemComponent
235
     */
236
    public get maxLeft() {
237
        return -this.width;
13✔
238
    }
239

240
    /**
241
     * Gets the maximum right position of the `list item`.
242
     * ```typescript
243
     * let maxRight = this.listItem.maxRight;
244
     * ```
245
     *
246
     * @memberof IgxListItemComponent
247
     */
248
    public get maxRight() {
249
        return this.width;
12✔
250
    }
251

252
    /** @hidden @internal */
253
    public get offsetWidthInRem() {
254
        return rem(this.element.offsetWidth);
72✔
255
    }
256

257
    /** @hidden @internal */
258
    public get offsetHeightInRem() {
259
        return rem(this.element.offsetHeight);
72✔
260
    }
261

262
    constructor(
263
        public list: IgxListBaseDirective,
2,052✔
264
        private elementRef: ElementRef,
2,052✔
265
        private _renderer: Renderer2) {
2,052✔
266
    }
267

268
    /**
269
     * Gets/Sets the `role` attribute of the `list item`.
270
     * ```typescript
271
     * let itemRole =  this.listItem.role;
272
     * ```
273
     *
274
     * @memberof IgxListItemComponent
275
     */
276
    @HostBinding('attr.role')
277
    @Input()
278
    public get role() {
279
        return this._role ? this._role : this.isHeader ? 'separator' : 'listitem';
15,677✔
280
    }
281

282
    public set role(val: string) {
283
        this._role = val;
1,153✔
284
    }
285

286
    /**
287
     * Sets/gets whether the `list item` is selected.
288
     * Selection is only applied to non-header items.
289
     * When selected, the CSS class 'igx-list__item-base--selected' is added to the item.
290
     * ```html
291
     * <igx-list-item [selected]="true">Selected Item</igx-list-item>
292
     * ```
293
     * ```typescript
294
     * let isSelected = this.listItem.selected;
295
     * this.listItem.selected = true;
296
     * ```
297
     *
298
     * @memberof IgxListItemComponent
299
     */
300
    @HostBinding('class.igx-list__item-base--selected')
301
    @Input({ transform: booleanAttribute })
302
    public get selected() {
303
        return this._selected && !this.isHeader;
15,677✔
304
    }
305

306
    public set selected(value: boolean) {
307
        this._selected = value;
4✔
308
    }
309

310
    /**
311
     * Indicates whether `list item` should have header style.
312
     * ```typescript
313
     * let headerStyle =  this.listItem.headerStyle;
314
     * ```
315
     *
316
     * @memberof IgxListItemComponent
317
     */
318
    @HostBinding('class.igx-list__header')
319
    public get headerStyle(): boolean {
320
        return this.isHeader;
15,672✔
321
    }
322

323
    /**
324
     * Applies the inner style of the `list item` if the item is not counted as header.
325
     * ```typescript
326
     * let innerStyle =  this.listItem.innerStyle;
327
     * ```
328
     *
329
     * @memberof IgxListItemComponent
330
     */
331
    @HostBinding('class.igx-list__item-base')
332
    public get innerStyle(): boolean {
333
        return !this.isHeader;
15,672✔
334
    }
335

336
    /**
337
     * Returns string value which describes the display mode of the `list item`.
338
     * ```typescript
339
     * let isHidden = this.listItem.display;
340
     * ```
341
     *
342
     * @memberof IgxListItemComponent
343
     */
344
    @HostBinding('style.display')
345
    public get display(): string {
346
        return this.hidden ? 'none' : '';
15,672✔
347
    }
348

349
    /**
350
     * @hidden
351
     */
352
    @HostListener('click', ['$event'])
353
    public clicked(evt) {
354
        this.list.itemClicked.emit({ item: this, event: evt, direction: this.lastPanDir });
59✔
355
        this.lastPanDir = IgxListPanState.NONE;
59✔
356
    }
357

358
    /**
359
     * @hidden
360
     */
361
    @HostListener('panstart')
362
    public panStart() {
363
        if (this.isTrue(this.isHeader)) {
21!
UNCOV
364
            return;
×
365
        }
366
        if (!this.isTrue(this.list.allowLeftPanning) && !this.isTrue(this.list.allowRightPanning)) {
21✔
367
            return;
2✔
368
        }
369

370
        this.list.startPan.emit({ item: this, direction: this.lastPanDir, keepitem: false });
19✔
371
    }
372

373
    /**
374
     * @hidden
375
     */
376
    @HostListener('pancancel')
377
    public panCancel() {
378
        this.resetPanPosition();
2✔
379
        this.list.endPan.emit({ item: this, direction: this.lastPanDir, keepItem: false });
2✔
380
    }
381

382
    /**
383
     * @hidden
384
     */
385
    @HostListener('panmove', ['$event'])
386
    public panMove(ev) {
387
        if (this.isTrue(this.isHeader)) {
21!
UNCOV
388
            return;
×
389
        }
390
        if (!this.isTrue(this.list.allowLeftPanning) && !this.isTrue(this.list.allowRightPanning)) {
21✔
391
            return;
2✔
392
        }
393
        const isPanningToLeft = ev.deltaX < 0;
19✔
394
        if (isPanningToLeft && this.isTrue(this.list.allowLeftPanning)) {
19✔
395
            this.showLeftPanTemplate();
9✔
396
            this.setContentElementLeft(Math.max(this.maxLeft, ev.deltaX));
9✔
397
        } else if (!isPanningToLeft && this.isTrue(this.list.allowRightPanning)) {
10✔
398
            this.showRightPanTemplate();
8✔
399
            this.setContentElementLeft(Math.min(this.maxRight, ev.deltaX));
8✔
400
        }
401
    }
402

403
    /**
404
     * @hidden
405
     */
406
    @HostListener('panend')
407
    public panEnd() {
408
        if (this.isTrue(this.isHeader)) {
17!
UNCOV
409
            return;
×
410
        }
411
        if (!this.isTrue(this.list.allowLeftPanning) && !this.isTrue(this.list.allowRightPanning)) {
17✔
412
            return;
2✔
413
        }
414

415
        // the translation offset of the current list item content
416
        const relativeOffset = this.panOffset;
15✔
417
        const widthTriggeringGrip = this.width * this.list.panEndTriggeringThreshold;
15✔
418

419
        if (relativeOffset === 0) {
15✔
420
            return; // no panning has occured
2✔
421
        }
422

423
        const dir = relativeOffset > 0 ? IgxListPanState.RIGHT : IgxListPanState.LEFT;
13✔
424
        this.lastPanDir = dir;
13✔
425

426
        const args = { item: this, direction: dir, keepItem: false };
13✔
427
        this.list.endPan.emit(args);
13✔
428

429
        const oldPanState = this._panState;
13✔
430
        if (Math.abs(relativeOffset) < widthTriggeringGrip) {
13✔
431
            this.resetPanPosition();
5✔
432
            this.list.resetPan.emit(this);
5✔
433
            return;
5✔
434
        }
435

436
        if (dir === IgxListPanState.LEFT) {
8✔
437
            this.list.leftPan.emit(args);
4✔
438
        } else {
439
            this.list.rightPan.emit(args);
4✔
440
        }
441

442
        if (args.keepItem === true) {
8✔
443
            this.setContentElementLeft(0);
2✔
444
            this._panState = IgxListPanState.NONE;
2✔
445
        } else {
446
            if (dir === IgxListPanState.LEFT) {
6✔
447
                this.setContentElementLeft(this.maxLeft);
3✔
448
                this._panState = IgxListPanState.LEFT;
3✔
449
            } else {
450
                this.setContentElementLeft(this.maxRight);
3✔
451
                this._panState = IgxListPanState.RIGHT;
3✔
452
            }
453
        }
454

455
        if (oldPanState !== this._panState) {
8✔
456
            const args2 = { oldState: oldPanState, newState: this._panState, item: this };
6✔
457
            this.list.panStateChange.emit(args2);
6✔
458
        }
459
        this.hideLeftAndRightPanTemplates();
8✔
460
    }
461

462
    /**
463
     * @hidden
464
     */
465
    private showLeftPanTemplate() {
466
        this.setLeftAndRightTemplatesVisibility('visible', 'hidden');
9✔
467
    }
468

469
    /**
470
     * @hidden
471
     */
472
    private showRightPanTemplate() {
473
        this.setLeftAndRightTemplatesVisibility('hidden', 'visible');
8✔
474
    }
475

476
    /**
477
     * @hidden
478
     */
479
    private hideLeftAndRightPanTemplates() {
480
        setTimeout(() => {
15✔
481
            this.setLeftAndRightTemplatesVisibility('hidden', 'hidden');
15✔
482
        }, 500);
483
    }
484

485
    /**
486
     * @hidden
487
     */
488
    private setLeftAndRightTemplatesVisibility(leftVisibility, rightVisibility) {
489
        if (this.leftPanningTemplateElement && this.leftPanningTemplateElement.nativeElement) {
32✔
490
            this.leftPanningTemplateElement.nativeElement.style.visibility = leftVisibility;
20✔
491
        }
492
        if (this.rightPanningTemplateElement && this.rightPanningTemplateElement.nativeElement) {
32✔
493
            this.rightPanningTemplateElement.nativeElement.style.visibility = rightVisibility;
20✔
494
        }
495
    }
496

497
    /**
498
     * @hidden
499
     */
500
    private setContentElementLeft(value: number) {
501
        this.panOffset = value;
32✔
502
        this.contentElement.style.transform = 'translateX(' + value + 'px)';
32✔
503
    }
504

505
    /**
506
     * @hidden
507
     */
508
    private isTrue(value: boolean): boolean {
509
        if (typeof (value) === 'boolean') {
149✔
510
            return value;
96✔
511
        } else {
512
            return value === 'true';
53✔
513
        }
514
    }
515

516
    /**
517
     * @hidden
518
     */
519
    private resetPanPosition() {
520
        this.setContentElementLeft(0);
7✔
521
        this._panState = IgxListPanState.NONE;
7✔
522
        this.hideLeftAndRightPanTemplates();
7✔
523
    }
524
}
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

© 2025 Coveralls, Inc