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

IgniteUI / igniteui-angular / 13331632524

14 Feb 2025 02:51PM CUT coverage: 22.015% (-69.6%) from 91.622%
13331632524

Pull #15372

github

web-flow
Merge d52d57714 into bcb78ae0a
Pull Request #15372: chore(*): test ci passing

1990 of 15592 branches covered (12.76%)

431 of 964 new or added lines in 18 files covered. (44.71%)

19956 existing lines in 307 files now uncovered.

6452 of 29307 relevant lines covered (22.02%)

249.17 hits per line

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

1.18
/projects/igniteui-angular/src/lib/chips/chips-area.component.ts
1
import {
2
    Component,
3
    ContentChildren,
4
    ChangeDetectorRef,
5
    EventEmitter,
6
    HostBinding,
7
    Input,
8
    IterableDiffer,
9
    IterableDiffers,
10
    Output,
11
    QueryList,
12
    DoCheck,
13
    AfterViewInit,
14
    OnDestroy,
15
    ElementRef
16
} from '@angular/core';
17
import {
18
    IgxChipComponent,
19
    IChipSelectEventArgs,
20
    IChipKeyDownEventArgs,
21
    IChipEnterDragAreaEventArgs,
22
    IBaseChipEventArgs
23
} from './chip.component';
24
import { IDropBaseEventArgs, IDragBaseEventArgs } from '../directives/drag-drop/drag-drop.directive';
25
import { takeUntil } from 'rxjs/operators';
26
import { Subject } from 'rxjs';
27
import { rem } from '../core/utils';
28

29
export interface IBaseChipsAreaEventArgs {
30
    originalEvent: IDragBaseEventArgs | IDropBaseEventArgs | KeyboardEvent | MouseEvent | TouchEvent;
31
    owner: IgxChipsAreaComponent;
32
}
33

34
export interface IChipsAreaReorderEventArgs extends IBaseChipsAreaEventArgs {
35
    chipsArray: IgxChipComponent[];
36
}
37

38
export interface IChipsAreaSelectEventArgs extends IBaseChipsAreaEventArgs {
39
    newSelection: IgxChipComponent[];
40
}
41

42
/**
43
 * The chip area allows you to perform more complex scenarios with chips that require interaction,
44
 * like dragging, selection, navigation, etc.
45
 *
46
 * @igxModule IgxChipsModule
47
 *
48
 * @igxTheme igx-chip-theme
49
 *
50
 * @igxKeywords chip area, chip
51
 *
52
 * @igxGroup display
53
 *
54
 * @example
55
 * ```html
56
 * <igx-chips-area>
57
 *    <igx-chip *ngFor="let chip of chipList" [id]="chip.id">
58
 *        <span>{{chip.text}}</span>
59
 *    </igx-chip>
60
 * </igx-chips-area>
61
 * ```
62
 */
63
@Component({
64
    selector: 'igx-chips-area',
65
    templateUrl: 'chips-area.component.html',
66
    standalone: true
67
})
68
export class IgxChipsAreaComponent implements DoCheck, AfterViewInit, OnDestroy {
2✔
69

70
    /**
71
     * Returns the `role` attribute of the chips area.
72
     *
73
     * @example
74
     * ```typescript
75
     * let chipsAreaRole = this.chipsArea.role;
76
     * ```
77
     */
78
     @HostBinding('attr.role')
UNCOV
79
     public role = 'listbox';
×
80

81
    /**
82
     * Returns the `aria-label` attribute of the chips area.
83
     *
84
     * @example
85
     * ```typescript
86
     * let ariaLabel = this.chipsArea.ariaLabel;
87
     * ```
88
     *
89
     */
90
     @HostBinding('attr.aria-label')
UNCOV
91
     public ariaLabel = 'chip area';
×
92

93
    /**
94
     * Sets the width of the `IgxChipsAreaComponent`.
95
     *
96
     * @example
97
     * ```html
98
     * <igx-chips-area #chipsArea [width]="300" [height]="10" (onReorder)="chipsOrderChanged($event)"></igx-chips-area>
99
     * ```
100
     */
101
    @Input()
102
    public width: number;
103

104
    /** @hidden @internal */
105
    @HostBinding('style.width.rem')
106
    public get _widthToRem() {
UNCOV
107
        return rem(this.width);
×
108
    }
109

110
    /**
111
     * Sets the height of the `IgxChipsAreaComponent`.
112
     *
113
     * @example
114
     * ```html
115
     * <igx-chips-area #chipsArea [width]="300" [height]="10" (onReorder)="chipsOrderChanged($event)"></igx-chips-area>
116
     * ```
117
     */
118
    @Input()
119
    public height: number;
120

121
    /** @hidden @internal */
122
    @HostBinding('style.height.rem')
123
    public get _heightToRem() {
UNCOV
124
        return rem(this.height);
×
125
    }
126

127
    /**
128
     * Emits an event when `IgxChipComponent`s in the `IgxChipsAreaComponent` should be reordered.
129
     * Returns an array of `IgxChipComponent`s.
130
     *
131
     * @example
132
     * ```html
133
     * <igx-chips-area #chipsArea [width]="'300'" [height]="'10'" (onReorder)="changedOrder($event)"></igx-chips-area>
134
     * ```
135
     */
136
    @Output()
UNCOV
137
    public reorder = new EventEmitter<IChipsAreaReorderEventArgs>();
×
138

139
    /**
140
     * Emits an event when an `IgxChipComponent` in the `IgxChipsAreaComponent` is selected/deselected.
141
     * Fired after the chips area is initialized if there are initially selected chips as well.
142
     * Returns an array of selected `IgxChipComponent`s and the `IgxChipAreaComponent`.
143
     *
144
     * @example
145
     * ```html
146
     * <igx-chips-area #chipsArea [width]="'300'" [height]="'10'" (selectionChange)="selection($event)"></igx-chips-area>
147
     * ```
148
     */
149
    @Output()
UNCOV
150
    public selectionChange = new EventEmitter<IChipsAreaSelectEventArgs>();
×
151

152
    /**
153
     * Emits an event when an `IgxChipComponent` in the `IgxChipsAreaComponent` is moved.
154
     *
155
     * @example
156
     * ```html
157
     * <igx-chips-area #chipsArea [width]="'300'" [height]="'10'" (moveStart)="moveStart($event)"></igx-chips-area>
158
     * ```
159
     */
160
    @Output()
UNCOV
161
    public moveStart = new EventEmitter<IBaseChipsAreaEventArgs>();
×
162

163
    /**
164
     * Emits an event after an `IgxChipComponent` in the `IgxChipsAreaComponent` is moved.
165
     *
166
     * @example
167
     * ```html
168
     * <igx-chips-area #chipsArea [width]="'300'" [height]="'10'" (moveEnd)="moveEnd($event)"></igx-chips-area>
169
     * ```
170
     */
171
    @Output()
UNCOV
172
    public moveEnd = new EventEmitter<IBaseChipsAreaEventArgs>();
×
173

174
    /**
175
     * Holds the `IgxChipComponent` in the `IgxChipsAreaComponent`.
176
     *
177
     * @example
178
     * ```typescript
179
     * ngAfterViewInit(){
180
     *    let chips = this.chipsArea.chipsList;
181
     * }
182
     * ```
183
     */
184
    @ContentChildren(IgxChipComponent, { descendants: true })
185
    public chipsList: QueryList<IgxChipComponent>;
186

UNCOV
187
    protected destroy$ = new Subject<boolean>();
×
188

189
    @HostBinding('class')
UNCOV
190
    private hostClass = 'igx-chip-area';
×
191

192
    private modifiedChipsArray: IgxChipComponent[];
UNCOV
193
    private _differ: IterableDiffer<IgxChipComponent> | null = null;
×
194

UNCOV
195
    constructor(public cdr: ChangeDetectorRef, public element: ElementRef,
×
UNCOV
196
        private _iterableDiffers: IterableDiffers) {
×
UNCOV
197
        this._differ = this._iterableDiffers.find([]).create(null);
×
198
    }
199

200
    /**
201
     * @hidden
202
     * @internal
203
     */
204
    public ngAfterViewInit() {
205
        // If we have initially selected chips through their inputs, we need to get them, because we cannot listen to their events yet.
UNCOV
206
        if (this.chipsList.length) {
×
UNCOV
207
            const selectedChips = this.chipsList.filter((item: IgxChipComponent) => item.selected);
×
UNCOV
208
            if (selectedChips.length) {
×
UNCOV
209
                this.selectionChange.emit({
×
210
                    originalEvent: null,
211
                    newSelection: selectedChips,
212
                    owner: this
213
                });
214
            }
215
        }
216
    }
217

218
    /**
219
     * @hidden
220
     * @internal
221
     */
222
    public ngDoCheck(): void {
UNCOV
223
        if (this.chipsList) {
×
UNCOV
224
            const changes = this._differ.diff(this.chipsList.toArray());
×
UNCOV
225
            if (changes) {
×
UNCOV
226
                changes.forEachAddedItem((addedChip) => {
×
UNCOV
227
                    addedChip.item.moveStart.pipe(takeUntil(addedChip.item.destroy$)).subscribe((args) => {
×
UNCOV
228
                        this.onChipMoveStart(args);
×
229
                    });
UNCOV
230
                    addedChip.item.moveEnd.pipe(takeUntil(addedChip.item.destroy$)).subscribe((args) => {
×
UNCOV
231
                        this.onChipMoveEnd(args);
×
232
                    });
UNCOV
233
                    addedChip.item.dragEnter.pipe(takeUntil(addedChip.item.destroy$)).subscribe((args) => {
×
UNCOV
234
                        this.onChipDragEnter(args);
×
235
                    });
UNCOV
236
                    addedChip.item.keyDown.pipe(takeUntil(addedChip.item.destroy$)).subscribe((args) => {
×
UNCOV
237
                        this.onChipKeyDown(args);
×
238
                    });
UNCOV
239
                    if (addedChip.item.selectable) {
×
UNCOV
240
                        addedChip.item.selectedChanging.pipe(takeUntil(addedChip.item.destroy$)).subscribe((args) => {
×
UNCOV
241
                            this.onChipSelectionChange(args);
×
242
                        });
243
                    }
244
                });
UNCOV
245
                this.modifiedChipsArray = this.chipsList.toArray();
×
246
            }
247
        }
248
    }
249

250
    /**
251
     * @hidden
252
     * @internal
253
     */
254
    public ngOnDestroy(): void {
UNCOV
255
        this.destroy$.next(true);
×
UNCOV
256
        this.destroy$.complete();
×
257
    }
258

259
    /**
260
     * @hidden
261
     * @internal
262
     */
263
    protected onChipKeyDown(event: IChipKeyDownEventArgs) {
UNCOV
264
        let orderChanged = false;
×
UNCOV
265
        const chipsArray = this.chipsList.toArray();
×
UNCOV
266
        const dragChipIndex = chipsArray.findIndex((el) => el === event.owner);
×
UNCOV
267
        if (event.originalEvent.shiftKey === true) {
×
UNCOV
268
            if (event.originalEvent.key === 'ArrowLeft' || event.originalEvent.key === 'Left') {
×
UNCOV
269
                orderChanged = this.positionChipAtIndex(dragChipIndex, dragChipIndex - 1, false, event.originalEvent);
×
UNCOV
270
                if (orderChanged) {
×
UNCOV
271
                    setTimeout(() => {
×
UNCOV
272
                        this.chipsList.get(dragChipIndex - 1).nativeElement.focus();
×
273
                    });
274
                }
UNCOV
275
            } else if (event.originalEvent.key === 'ArrowRight' || event.originalEvent.key === 'Right') {
×
UNCOV
276
                orderChanged = this.positionChipAtIndex(dragChipIndex, dragChipIndex + 1, true, event.originalEvent);
×
277
            }
278
        } else {
UNCOV
279
            if ((event.originalEvent.key === 'ArrowLeft' || event.originalEvent.key === 'Left') && dragChipIndex > 0) {
×
UNCOV
280
                chipsArray[dragChipIndex - 1].nativeElement.focus();
×
UNCOV
281
            } else if ((event.originalEvent.key === 'ArrowRight' || event.originalEvent.key === 'Right') &&
×
282
                dragChipIndex < chipsArray.length - 1) {
UNCOV
283
                chipsArray[dragChipIndex + 1].nativeElement.focus();
×
284
            }
285
        }
286
    }
287

288
    /**
289
     * @hidden
290
     * @internal
291
     */
292
    protected onChipMoveStart(event: IBaseChipEventArgs) {
UNCOV
293
        this.moveStart.emit({
×
294
            originalEvent: event.originalEvent,
295
            owner: this
296
        });
297
    }
298

299
    /**
300
     * @hidden
301
     * @internal
302
     */
303
    protected onChipMoveEnd(event: IBaseChipEventArgs) {
UNCOV
304
        this.moveEnd.emit({
×
305
            originalEvent: event.originalEvent,
306
            owner: this
307
        });
308
    }
309

310
    /**
311
     * @hidden
312
     * @internal
313
     */
314
    protected onChipDragEnter(event: IChipEnterDragAreaEventArgs) {
UNCOV
315
        const dropChipIndex = this.chipsList.toArray().findIndex((el) => el === event.owner);
×
UNCOV
316
        const dragChipIndex = this.chipsList.toArray().findIndex((el) => el === event.dragChip);
×
UNCOV
317
        if (dragChipIndex < dropChipIndex) {
×
318
            // from the left to right
UNCOV
319
            this.positionChipAtIndex(dragChipIndex, dropChipIndex, true, event.originalEvent);
×
320
        } else {
321
            // from the right to left
UNCOV
322
            this.positionChipAtIndex(dragChipIndex, dropChipIndex, false, event.originalEvent);
×
323
        }
324
    }
325

326
    /**
327
     * @hidden
328
     * @internal
329
     */
330
    protected positionChipAtIndex(chipIndex, targetIndex, shiftRestLeft, originalEvent) {
UNCOV
331
        if (chipIndex < 0 || this.chipsList.length <= chipIndex ||
×
332
            targetIndex < 0 || this.chipsList.length <= targetIndex) {
UNCOV
333
            return false;
×
334
        }
335

UNCOV
336
        const chipsArray = this.chipsList.toArray();
×
UNCOV
337
        const result: IgxChipComponent[] = [];
×
UNCOV
338
        for (let i = 0; i < chipsArray.length; i++) {
×
UNCOV
339
            if (shiftRestLeft) {
×
UNCOV
340
                if (chipIndex <= i && i < targetIndex) {
×
UNCOV
341
                    result.push(chipsArray[i + 1]);
×
UNCOV
342
                } else if (i === targetIndex) {
×
UNCOV
343
                    result.push(chipsArray[chipIndex]);
×
344
                } else {
UNCOV
345
                    result.push(chipsArray[i]);
×
346
                }
347
            } else {
UNCOV
348
                if (targetIndex < i && i <= chipIndex) {
×
UNCOV
349
                    result.push(chipsArray[i - 1]);
×
UNCOV
350
                } else if (i === targetIndex) {
×
UNCOV
351
                    result.push(chipsArray[chipIndex]);
×
352
                } else {
UNCOV
353
                    result.push(chipsArray[i]);
×
354
                }
355
            }
356
        }
UNCOV
357
        this.modifiedChipsArray = result;
×
358

UNCOV
359
        const eventData: IChipsAreaReorderEventArgs = {
×
360
            chipsArray: this.modifiedChipsArray,
361
            originalEvent,
362
            owner: this
363
        };
UNCOV
364
        this.reorder.emit(eventData);
×
UNCOV
365
        return true;
×
366
    }
367

368
    /**
369
     * @hidden
370
     * @internal
371
     */
372
    protected onChipSelectionChange(event: IChipSelectEventArgs) {
UNCOV
373
        let selectedChips = this.chipsList.filter((chip) => chip.selected);
×
UNCOV
374
        if (event.selected && !selectedChips.includes(event.owner)) {
×
UNCOV
375
            selectedChips.push(event.owner);
×
UNCOV
376
        } else if (!event.selected && selectedChips.includes(event.owner)) {
×
UNCOV
377
            selectedChips = selectedChips.filter((chip) => chip.id !== event.owner.id);
×
378
        }
UNCOV
379
        this.selectionChange.emit({
×
380
            originalEvent: event.originalEvent,
381
            newSelection: selectedChips,
382
            owner: this
383
        });
384
    }
385
}
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