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

IgniteUI / igniteui-angular / 5608669271

pending completion
5608669271

push

github

web-flow
feat(combo): Exported filter strategy for non-diacritic filter (#13071)

* feat(combo): Exported filter strategy for non-diacritic filter

Closes #12876

* refactor: Renamed export and added CHANGELOG entry

---------

Co-authored-by: Nikolay Alipiev <nikolay.alipiev@gmail.com>
Co-authored-by: Konstantin Dinev <kdinev@mail.bw.edu>

15319 of 17993 branches covered (85.14%)

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

26843 of 29091 relevant lines covered (92.27%)

29633.98 hits per line

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

90.71
/projects/igniteui-angular/src/lib/buttonGroup/buttonGroup.component.ts
1
import { NgFor, NgIf } from '@angular/common';
2
import {
3
    AfterContentInit,
4
    AfterViewInit,
5
    Component,
6
    ContentChildren,
7
    ChangeDetectorRef,
8
    EventEmitter,
9
    HostBinding,
10
    Inject,
11
    Input,
12
    Output,
13
    Optional,
14
    QueryList,
15
    Renderer2,
2✔
16
    ViewChildren,
17
    OnDestroy
18
} from '@angular/core';
19
import { Subject } from 'rxjs';
2✔
20
import { IgxButtonDirective } from '../directives/button/button.directive';
21
import { IgxRippleDirective } from '../directives/ripple/ripple.directive';
22

23
import { takeUntil } from 'rxjs/operators';
24
import { DisplayDensityBase, DisplayDensityToken, IDisplayDensityOptions } from '../core/density';
25
import { IBaseEventArgs } from '../core/utils';
26
import { mkenum } from '../core/utils';
27
import { IgxIconComponent } from '../icon/icon.component';
28

29
/**
30
 * Determines the Button Group alignment
31
 */
32
export const ButtonGroupAlignment = mkenum({
33
    horizontal: 'horizontal',
34
    vertical: 'vertical'
35
});
36
export type ButtonGroupAlignment = typeof ButtonGroupAlignment[keyof typeof ButtonGroupAlignment];
37

38
let NEXT_ID = 0;
39

40
/**
2✔
41
 * **Ignite UI for Angular Button Group** -
42
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/buttongroup.html)
43
 *
44
 * The Ignite UI Button Group displays a group of buttons either vertically or horizontally.  The group supports
45
 * single, multiple and toggle selection.
729✔
46
 *
47
 * Example:
48
 * ```html
4!
49
 * <igx-buttongroup multiSelection="true" [values]="fontOptions">
50
 * </igx-buttongroup>
51
 * ```
52
 * The `fontOptions` value shown above is defined as:
53
 * ```typescript
54
 * this.fontOptions = [
55
 *   { icon: 'format_bold', selected: false },
56
 *   { icon: 'format_italic', selected: false },
57
 *   { icon: 'format_underlined', selected: false }];
58
 * ```
59
 */
60
@Component({
61
    selector: 'igx-buttongroup',
225✔
62
    templateUrl: 'buttongroup-content.component.html',
63
    standalone: true,
64
    imports: [NgFor, IgxButtonDirective, IgxRippleDirective, NgIf, IgxIconComponent]
426✔
65
})
66
export class IgxButtonGroupComponent extends DisplayDensityBase implements AfterContentInit, AfterViewInit, OnDestroy {
67
    /**
×
68
     * A collection containing all buttons inside the button group.
×
69
     */
×
70
    public get buttons(): IgxButtonDirective[] {
×
71
        return [...this.viewButtons.toArray(), ...this.templateButtons.toArray()];
72
    }
73

74
    /**
75
     * An @Input property that sets the value of the `id` attribute. If not set it will be automatically generated.
7✔
76
     * ```html
77
     *  <igx-buttongroup [id]="'igx-dialog-56'" [multiSelection]="!multi" [values]="alignOptions">
78
     * ```
79
     */
80
    @HostBinding('attr.id')
81
    @Input()
82
    public id = `igx-buttongroup-${NEXT_ID++}`;
83

84
    /**
85
     * @hidden
86
     */
87
    @HostBinding('style.zIndex')
88
    public zIndex = 0;
2✔
89

90
    /**
91
     * Allows you to set a style using the `itemContentCssClass` input.
92
     * The value should be the CSS class name that will be applied to the button group.
93
     * ```typescript
94
     * public style1 = "styleClass";
95
     *  //..
96
     * ```
97
     *  ```html
98
     * <igx-buttongroup [itemContentCssClass]="style1" [multiSelection]="!multi" [values]="alignOptions">
99
     * ```
100
     */
101
    @Input()
102
    public set itemContentCssClass(value: string) {
103
        this._itemContentCssClass = value || this._itemContentCssClass;
104
    }
105

106
    /**
709✔
107
     * Returns the CSS class of the item content of the `IgxButtonGroup`.
108
     * ```typescript
109
     *  @ViewChild("MyChild")
88✔
110
     * public buttonG: IgxButtonGroupComponent;
88✔
111
     * ngAfterViewInit(){
88✔
112
     *    let buttonSelect = this.buttonG.itemContentCssClass;
88✔
113
     * }
88✔
114
     * ```
88✔
115
     */
88✔
116
    public get itemContentCssClass(): string {
88✔
117
        return this._itemContentCssClass;
88✔
118
    }
119

120
    /**
121
     * An @Input property that enables selecting multiple buttons. By default, multi-selection is false.
88✔
122
     * ```html
88✔
123
     * <igx-buttongroup [multiSelection]="false" [alignment]="alignment"></igx-buttongroup>
88✔
124
     * ```
88✔
125
     */
88✔
126
    @Input() public multiSelection = false;
127

128
    /**
129
     * An @Input property that allows setting the buttons in the button group.
130
     * ```typescript
131
     *  public ngOnInit() {
132
     *      this.cities = [
133
     *        new Button({
134
     *          label: "Sofia"
135
     *      }),
136
     *        new Button({
137
     *          label: "London"
138
     *      }),
103✔
139
     *        new Button({
140
     *          label: "New York",
141
     *          selected: true
142
     *      }),
143
     *        new Button({
144
     *          label: "Tokyo"
145
     *      })
146
     *  ];
147
     *  }
148
     *  //..
149
     * ```
150
     * ```html
151
     *  <igx-buttongroup [multiSelection]="false" [values]="cities"></igx-buttongroup>
152
     * ```
153
     */
154
    @Input() public values: any;
54✔
155

2✔
156
    /**
157
     * An @Input property that allows you to disable the `igx-buttongroup` component. By default it's false.
52✔
158
     * ```html
52✔
159
     * <igx-buttongroup [disabled]="true" [multiSelection]="multi" [values]="fontOptions"></igx-buttongroup>
160
     * ```
161
     */
162
    @Input()
163
    public get disabled(): boolean {
164
        return this._disabled;
165
    }
107✔
166
    public set disabled(value: boolean) {
107✔
167
        if (this._disabled !== value) {
104✔
168
            this._disabled = value;
169

107✔
170
            if (this.viewButtons && this.templateButtons) {
107✔
171
                this.buttons.forEach((b) => (b.disabled = this._disabled));
107✔
172
            }
107✔
173
        }
27✔
174
    }
175

176
    /**
107✔
177
     * Allows you to set the button group alignment.
19✔
178
     * Available options are `ButtonGroupAlignment.horizontal` (default) and `ButtonGroupAlignment.vertical`.
50✔
179
     * ```typescript
19✔
180
     * public alignment = ButtonGroupAlignment.vertical;
181
     * //..
182
     * ```
183
     * ```html
184
     * <igx-buttongroup [multiSelection]="false" [values]="cities" [alignment]="alignment"></igx-buttongroup>
185
     * ```
186
     */
187
    @Input()
188
    public set alignment(value: ButtonGroupAlignment) {
189
        this._isVertical = value === ButtonGroupAlignment.vertical;
190
    }
191
    /**
192
     * Returns the alignment of the `igx-buttongroup`.
193
     * ```typescript
194
     * @ViewChild("MyChild")
195
     * public buttonG: IgxButtonGroupComponent;
196
     * ngAfterViewInit(){
197
     *    let buttonAlignment = this.buttonG.alignment;
198
     * }
125✔
199
     * ```
2✔
200
     */
201
    public get alignment(): ButtonGroupAlignment {
123✔
202
        return this._isVertical ? ButtonGroupAlignment.vertical : ButtonGroupAlignment.horizontal;
123✔
203
    }
123✔
204

123✔
205
    /**
123✔
206
     * An @Ouput property that emits an event when a button is selected.
123✔
207
     * ```typescript
123✔
208
     * @ViewChild("toast")
9✔
209
     * private toast: IgxToastComponent;
210
     * public selectedHandler(buttongroup) {
211
     *     this.toast.open()
212
     * }
213
     *  //...
214
     * ```
215
     * ```html
88✔
216
     * <igx-buttongroup #MyChild [multiSelection]="!multi" (selected)="selectedHandler($event)"></igx-buttongroup>
146✔
217
     * <igx-toast #toast>You have made a selection!</igx-toast>
19✔
218
     * ```
219
     */
220
    @Output()
221
    public selected = new EventEmitter<IButtonGroupEventArgs>();
222

223
    /**
224
     * An @Ouput property that emits an event when a button is deselected.
225
     * ```typescript
90✔
226
     *  @ViewChild("toast")
227
     *  private toast: IgxToastComponent;
90✔
228
     *  public deselectedHandler(buttongroup){
90✔
229
     *     this.toast.open()
230
     * }
90✔
231
     *  //...
201✔
232
     * ```
201✔
233
     * ```html
201!
234
     * <igx-buttongroup> #MyChild [multiSelection]="multi" (deselected)="deselectedHandler($event)"></igx-buttongroup>
×
235
     * <igx-toast #toast>You have deselected a button!</igx-toast>
236
     * ```
201✔
237
     */
50✔
238
    @Output()
239
    public deselected = new EventEmitter<IButtonGroupEventArgs>();
201✔
240

201✔
241
    @ViewChildren(IgxButtonDirective) private viewButtons: QueryList<IgxButtonDirective>;
242
    @ContentChildren(IgxButtonDirective) private templateButtons: QueryList<IgxButtonDirective>;
57✔
243

244
    /**
245
     * Returns true if the `igx-buttongroup` alignment is vertical.
90✔
246
     * Note that in order for the accessor to work correctly the property should be set explicitly.
90✔
247
     * ```html
90✔
248
     * <igx-buttongroup #MyChild [alignment]="alignment" [values]="alignOptions">
90✔
249
     * ```
250
     * ```typescript
251
     * //...
252
     * @ViewChild("MyChild")
253
     * private buttonG: IgxButtonGroupComponent;
254
     * ngAfterViewInit(){
62✔
255
     *    let orientation = this.buttonG.isVertical;
62✔
256
     * }
62✔
257
     * ```
62✔
258
     */
62✔
259
    public get isVertical(): boolean {
62✔
260
        return this._isVertical;
261
    }
262

263
    /**
264
     * @hidden
265
     */
36✔
266
    public selectedIndexes: number[] = [];
36✔
267

31✔
268
    protected buttonClickNotifier$ = new Subject<boolean>();
64✔
269
    protected buttonSelectedNotifier$ = new Subject<boolean>();
10✔
270
    protected queryListNotifier$ = new Subject<boolean>();
271

272
    private _isVertical: boolean;
273
    private _itemContentCssClass: string;
36✔
274
    private _disabled = false;
26✔
275

26✔
276
    constructor(
277
        private _cdr: ChangeDetectorRef,
278
        private _renderer: Renderer2,
10✔
279
        @Optional() @Inject(DisplayDensityToken) protected _displayDensityOptions: IDisplayDensityOptions
10✔
280
    ) {
281
        super(_displayDensityOptions);
282
    }
2✔
283

284
    /**
285
     * Gets the selected button/buttons.
286
     * ```typescript
287
     * @ViewChild("MyChild")
2✔
288
     * private buttonG: IgxButtonGroupComponent;
289
     * ngAfterViewInit(){
290
     *    let selectedButton = this.buttonG.selectedButtons;
291
     * }
292
     * ```
293
     */
294
    public get selectedButtons(): IgxButtonDirective[] {
295
        return this.buttons.filter((_, i) => this.selectedIndexes.indexOf(i) !== -1);
296
    }
297

298
    /**
299
     * Selects a button by its index.
300
     * ```typescript
301
     * @ViewChild("MyChild")
2✔
302
     * private buttonG: IgxButtonGroupComponent;
303
     * ngAfterViewInit(){
304
     *    this.buttonG.selectButton(2);
305
     *    this.cdr.detectChanges();
306
     * }
307
     * ```
308
     *
309
     * @memberOf {@link IgxButtonGroupComponent}
310
     */
311
    public selectButton(index: number) {
312
        if (index >= this.buttons.length || index < 0) {
313
            return;
314
        }
315

316
        const button = this.buttons[index];
317
        button.select();
318
    }
319

320
    /**
321
     * @hidden
322
     * @internal
323
     */
324
    public updateSelected(index: number) {
325
        const button = this.buttons[index];
326

327
        if (this.selectedIndexes.indexOf(index) === -1) {
328
            this.selectedIndexes.push(index);
329
        }
330

331
        this._renderer.setAttribute(button.nativeElement, 'aria-pressed', 'true');
332
        this._renderer.addClass(button.nativeElement, 'igx-button-group__item--selected');
333

334
        const indexInViewButtons = this.viewButtons.toArray().indexOf(button);
335
        if (indexInViewButtons !== -1) {
336
            this.values[indexInViewButtons].selected = true;
337
        }
338

339
        // deselect other buttons if multiSelection is not enabled
340
        if (!this.multiSelection && this.selectedIndexes.length > 1) {
341
            this.buttons.forEach((_, i) => {
342
                if (i !== index && this.selectedIndexes.indexOf(i) !== -1) {
343
                    this.deselectButton(i);
344
                }
345
            });
346
        }
347
    }
348

349
    /**
350
     * Deselects a button by its index.
351
     * ```typescript
352
     * @ViewChild("MyChild")
353
     * private buttonG: IgxButtonGroupComponent;
354
     * ngAfterViewInit(){
355
     *    this.buttonG.deselectButton(2);
356
     *    this.cdr.detectChanges();
357
     * }
358
     * ```
359
     *
360
     * @memberOf {@link IgxButtonGroupComponent}
361
     */
362
    public deselectButton(index: number) {
363
        if (index >= this.buttons.length || index < 0) {
364
            return;
365
        }
366

367
        const button = this.buttons[index];
368
        this.selectedIndexes.splice(this.selectedIndexes.indexOf(index), 1);
369

370
        this._renderer.setAttribute(button.nativeElement, 'aria-pressed', 'false');
371
        this._renderer.removeClass(button.nativeElement, 'igx-button-group__item--selected');
372
        button.deselect();
373

374
        const indexInViewButtons = this.viewButtons.toArray().indexOf(button);
375
        if (indexInViewButtons !== -1) {
376
            this.values[indexInViewButtons].selected = false;
377
        }
378
    }
379

380
    /**
381
     * @hidden
382
     */
383
    public ngAfterContentInit() {
384
        this.templateButtons.forEach((button) => {
385
            if (!button.initialDensity) {
386
                button.displayDensity = this.displayDensity;
387
            }
388
        });
389
    }
390

391
    /**
392
     * @hidden
393
     */
394
    public ngAfterViewInit() {
395
        const initButtons = () => {
396
            // Cancel any existing buttonClick subscriptions
397
            this.buttonClickNotifier$.next();
398

399
            this.selectedIndexes.splice(0, this.selectedIndexes.length);
400

401
            // initial configuration
402
            this.buttons.forEach((button, index) => {
403
                const buttonElement = button.nativeElement;
404
                this._renderer.addClass(buttonElement, 'igx-button-group__item');
405

406
                if (this.disabled) {
407
                    button.disabled = true;
408
                }
409

410
                if (button.selected) {
411
                    this.updateSelected(index);
412
                }
413

414
                button.buttonClick.pipe(takeUntil(this.buttonClickNotifier$)).subscribe((_) => this._clickHandler(index));
415
                button.buttonSelected
416
                    .pipe(takeUntil(this.buttonSelectedNotifier$))
417
                    .subscribe((_) => this.updateSelected(index));
418
            });
419
        };
420

421
        this.viewButtons.changes.pipe(takeUntil(this.queryListNotifier$)).subscribe(() => initButtons());
422
        this.templateButtons.changes.pipe(takeUntil(this.queryListNotifier$)).subscribe(() => initButtons());
423
        initButtons();
424

425
        this._cdr.detectChanges();
426
    }
427

428
    /**
429
     * @hidden
430
     */
431
    public ngOnDestroy() {
432
        this.buttonClickNotifier$.next();
433
        this.buttonClickNotifier$.complete();
434

435
        this.buttonSelectedNotifier$.next();
436
        this.buttonSelectedNotifier$.complete();
437

438
        this.queryListNotifier$.next();
439
        this.queryListNotifier$.complete();
440
    }
441

442
    /**
443
     * @hidden
444
     */
445
    public _clickHandler(index: number) {
446
        const button = this.buttons[index];
447

448
        if (!this.multiSelection) {
449
            this.buttons.forEach((b, i) => {
450
                if (i !== index && this.selectedIndexes.indexOf(i) !== -1) {
451
                    this.deselected.emit({ button: b, index: i });
452
                }
453
            });
454
        }
455

456
        if (this.selectedIndexes.indexOf(index) === -1) {
457
            this.selectButton(index);
458
            this.selected.emit({ button, index });
459
        } else {
460
            this.deselectButton(index);
461
            this.deselected.emit({ button, index });
462
        }
463
    }
464
}
465

466
export interface IButtonGroupEventArgs extends IBaseEventArgs {
467
    button: IgxButtonDirective;
468
    index: number;
469
}
470

471
/**
472
 * @hidden
473
 */
474

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