UNPKG

igniteui-angular-sovn

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

466 lines (415 loc) 14.1 kB
import { NgFor, NgIf } from '@angular/common'; import { AfterContentInit, AfterViewInit, Component, ContentChildren, ChangeDetectorRef, EventEmitter, HostBinding, Inject, Input, Output, Optional, QueryList, Renderer2, ViewChildren, OnDestroy } from '@angular/core'; import { Subject } from 'rxjs'; import { IgxButtonDirective } from '../directives/button/button.directive'; import { IgxRippleDirective } from '../directives/ripple/ripple.directive'; import { takeUntil } from 'rxjs/operators'; import { DisplayDensityBase, DisplayDensityToken, IDisplayDensityOptions } from '../core/density'; import { IBaseEventArgs } from '../core/utils'; import { mkenum } from '../core/utils'; import { IgxIconComponent } from '../icon/icon.component'; /** * Determines the Button Group alignment */ export const ButtonGroupAlignment = mkenum({ horizontal: 'horizontal', vertical: 'vertical' }); export type ButtonGroupAlignment = typeof ButtonGroupAlignment[keyof typeof ButtonGroupAlignment]; let NEXT_ID = 0; /** * **Ignite UI for Angular Button Group** - * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/buttongroup.html) * * The Ignite UI Button Group displays a group of buttons either vertically or horizontally. The group supports * single, multiple and toggle selection. * * Example: * ```html * <igx-buttongroup multiSelection="true" [values]="fontOptions"> * </igx-buttongroup> * ``` * The `fontOptions` value shown above is defined as: * ```typescript * this.fontOptions = [ * { icon: 'format_bold', selected: false }, * { icon: 'format_italic', selected: false }, * { icon: 'format_underlined', selected: false }]; * ``` */ @Component({ selector: 'igx-buttongroup', templateUrl: 'buttongroup-content.component.html', standalone: true, imports: [NgFor, IgxButtonDirective, IgxRippleDirective, NgIf, IgxIconComponent] }) export class IgxButtonGroupComponent extends DisplayDensityBase implements AfterContentInit, AfterViewInit, OnDestroy { /** * A collection containing all buttons inside the button group. */ public get buttons(): IgxButtonDirective[] { return [...this.viewButtons.toArray(), ...this.templateButtons.toArray()]; } /** * An @Input property that sets the value of the `id` attribute. If not set it will be automatically generated. * ```html * <igx-buttongroup [id]="'igx-dialog-56'" [multiSelection]="!multi" [values]="alignOptions"> * ``` */ @HostBinding('attr.id') @Input() public id = `igx-buttongroup-${NEXT_ID++}`; /** * @hidden */ @HostBinding('style.zIndex') public zIndex = 0; /** * Allows you to set a style using the `itemContentCssClass` input. * The value should be the CSS class name that will be applied to the button group. * ```typescript * public style1 = "styleClass"; * //.. * ``` * ```html * <igx-buttongroup [itemContentCssClass]="style1" [multiSelection]="!multi" [values]="alignOptions"> * ``` */ @Input() public set itemContentCssClass(value: string) { this._itemContentCssClass = value || this._itemContentCssClass; } /** * Returns the CSS class of the item content of the `IgxButtonGroup`. * ```typescript * @ViewChild("MyChild") * public buttonG: IgxButtonGroupComponent; * ngAfterViewInit(){ * let buttonSelect = this.buttonG.itemContentCssClass; * } * ``` */ public get itemContentCssClass(): string { return this._itemContentCssClass; } /** * An @Input property that enables selecting multiple buttons. By default, multi-selection is false. * ```html * <igx-buttongroup [multiSelection]="false" [alignment]="alignment"></igx-buttongroup> * ``` */ @Input() public multiSelection = false; /** * An @Input property that allows setting the buttons in the button group. * ```typescript * public ngOnInit() { * this.cities = [ * new Button({ * label: "Sofia" * }), * new Button({ * label: "London" * }), * new Button({ * label: "New York", * selected: true * }), * new Button({ * label: "Tokyo" * }) * ]; * } * //.. * ``` * ```html * <igx-buttongroup [multiSelection]="false" [values]="cities"></igx-buttongroup> * ``` */ @Input() public values: any; /** * An @Input property that allows you to disable the `igx-buttongroup` component. By default it's false. * ```html * <igx-buttongroup [disabled]="true" [multiSelection]="multi" [values]="fontOptions"></igx-buttongroup> * ``` */ @Input() public get disabled(): boolean { return this._disabled; } public set disabled(value: boolean) { if (this._disabled !== value) { this._disabled = value; if (this.viewButtons && this.templateButtons) { this.buttons.forEach((b) => (b.disabled = this._disabled)); } } } /** * Allows you to set the button group alignment. * Available options are `ButtonGroupAlignment.horizontal` (default) and `ButtonGroupAlignment.vertical`. * ```typescript * public alignment = ButtonGroupAlignment.vertical; * //.. * ``` * ```html * <igx-buttongroup [multiSelection]="false" [values]="cities" [alignment]="alignment"></igx-buttongroup> * ``` */ @Input() public set alignment(value: ButtonGroupAlignment) { this._isVertical = value === ButtonGroupAlignment.vertical; } /** * Returns the alignment of the `igx-buttongroup`. * ```typescript * @ViewChild("MyChild") * public buttonG: IgxButtonGroupComponent; * ngAfterViewInit(){ * let buttonAlignment = this.buttonG.alignment; * } * ``` */ public get alignment(): ButtonGroupAlignment { return this._isVertical ? ButtonGroupAlignment.vertical : ButtonGroupAlignment.horizontal; } /** * An @Ouput property that emits an event when a button is selected. * ```typescript * @ViewChild("toast") * private toast: IgxToastComponent; * public selectedHandler(buttongroup) { * this.toast.open() * } * //... * ``` * ```html * <igx-buttongroup #MyChild [multiSelection]="!multi" (selected)="selectedHandler($event)"></igx-buttongroup> * <igx-toast #toast>You have made a selection!</igx-toast> * ``` */ @Output() public selected = new EventEmitter<IButtonGroupEventArgs>(); /** * An @Ouput property that emits an event when a button is deselected. * ```typescript * @ViewChild("toast") * private toast: IgxToastComponent; * public deselectedHandler(buttongroup){ * this.toast.open() * } * //... * ``` * ```html * <igx-buttongroup> #MyChild [multiSelection]="multi" (deselected)="deselectedHandler($event)"></igx-buttongroup> * <igx-toast #toast>You have deselected a button!</igx-toast> * ``` */ @Output() public deselected = new EventEmitter<IButtonGroupEventArgs>(); @ViewChildren(IgxButtonDirective) private viewButtons: QueryList<IgxButtonDirective>; @ContentChildren(IgxButtonDirective) private templateButtons: QueryList<IgxButtonDirective>; /** * Returns true if the `igx-buttongroup` alignment is vertical. * Note that in order for the accessor to work correctly the property should be set explicitly. * ```html * <igx-buttongroup #MyChild [alignment]="alignment" [values]="alignOptions"> * ``` * ```typescript * //... * @ViewChild("MyChild") * private buttonG: IgxButtonGroupComponent; * ngAfterViewInit(){ * let orientation = this.buttonG.isVertical; * } * ``` */ public get isVertical(): boolean { return this._isVertical; } /** * @hidden */ public selectedIndexes: number[] = []; protected buttonClickNotifier$ = new Subject<boolean>(); protected buttonSelectedNotifier$ = new Subject<boolean>(); protected queryListNotifier$ = new Subject<boolean>(); private _isVertical: boolean; private _itemContentCssClass: string; private _disabled = false; constructor( private _cdr: ChangeDetectorRef, private _renderer: Renderer2, @Optional() @Inject(DisplayDensityToken) protected _displayDensityOptions: IDisplayDensityOptions ) { super(_displayDensityOptions); } /** * Gets the selected button/buttons. * ```typescript * @ViewChild("MyChild") * private buttonG: IgxButtonGroupComponent; * ngAfterViewInit(){ * let selectedButton = this.buttonG.selectedButtons; * } * ``` */ public get selectedButtons(): IgxButtonDirective[] { return this.buttons.filter((_, i) => this.selectedIndexes.indexOf(i) !== -1); } /** * Selects a button by its index. * ```typescript * @ViewChild("MyChild") * private buttonG: IgxButtonGroupComponent; * ngAfterViewInit(){ * this.buttonG.selectButton(2); * this.cdr.detectChanges(); * } * ``` * * @memberOf {@link IgxButtonGroupComponent} */ public selectButton(index: number) { if (index >= this.buttons.length || index < 0) { return; } const button = this.buttons[index]; button.select(); } /** * @hidden * @internal */ public updateSelected(index: number) { const button = this.buttons[index]; if(this.selectedIndexes.indexOf(index) === -1) { this.selectedIndexes.push(index); this.selected.emit({ button, index }); } this._renderer.setAttribute(button.nativeElement, 'aria-pressed', 'true'); this._renderer.addClass(button.nativeElement, 'igx-button-group__item--selected'); const indexInViewButtons = this.viewButtons.toArray().indexOf(button); if (indexInViewButtons !== -1) { this.values[indexInViewButtons].selected = true; } // deselect other buttons if multiSelection is not enabled if (!this.multiSelection && this.selectedIndexes.length > 1) { this.buttons.forEach((_, i) => { if (i !== index && this.selectedIndexes.indexOf(i) !== -1) { this.deselectButton(i); } }); } } /** * Deselects a button by its index. * ```typescript * @ViewChild("MyChild") * private buttonG: IgxButtonGroupComponent; * ngAfterViewInit(){ * this.buttonG.deselectButton(2); * this.cdr.detectChanges(); * } * ``` * * @memberOf {@link IgxButtonGroupComponent} */ public deselectButton(index: number) { if (index >= this.buttons.length || index < 0) { return; } const button = this.buttons[index]; this.selectedIndexes.splice(this.selectedIndexes.indexOf(index), 1); this._renderer.setAttribute(button.nativeElement, 'aria-pressed', 'false'); this._renderer.removeClass(button.nativeElement, 'igx-button-group__item--selected'); button.deselect(); const indexInViewButtons = this.viewButtons.toArray().indexOf(button); if (indexInViewButtons !== -1) { this.values[indexInViewButtons].selected = false; } this.deselected.emit({ button, index }); } /** * @hidden */ public ngAfterContentInit() { this.templateButtons.forEach((button) => { if (!button.initialDensity) { button.displayDensity = this.displayDensity; } }); } /** * @hidden */ public ngAfterViewInit() { const initButtons = () => { // Cancel any existing buttonClick subscriptions this.buttonClickNotifier$.next(); this.selectedIndexes.splice(0, this.selectedIndexes.length); // initial configuration this.buttons.forEach((button, index) => { const buttonElement = button.nativeElement; this._renderer.addClass(buttonElement, 'igx-button-group__item'); if (this.disabled) { button.disabled = true; } if (button.selected) { this.updateSelected(index); } button.buttonClick.pipe(takeUntil(this.buttonClickNotifier$)).subscribe((_) => this._clickHandler(index)); button.buttonSelected .pipe(takeUntil(this.buttonSelectedNotifier$)) .subscribe((_) => this.updateSelected(index)); }); }; this.viewButtons.changes.pipe(takeUntil(this.queryListNotifier$)).subscribe(() => initButtons()); this.templateButtons.changes.pipe(takeUntil(this.queryListNotifier$)).subscribe(() => initButtons()); initButtons(); this._cdr.detectChanges(); } /** * @hidden */ public ngOnDestroy() { this.buttonClickNotifier$.next(); this.buttonClickNotifier$.complete(); this.buttonSelectedNotifier$.next(); this.buttonSelectedNotifier$.complete(); this.queryListNotifier$.next(); this.queryListNotifier$.complete(); } /** * @hidden */ public _clickHandler(i: number) { if (this.selectedIndexes.indexOf(i) === -1) { this.selectButton(i); } else { this.deselectButton(i); } } } export interface IButtonGroupEventArgs extends IBaseEventArgs { button: IgxButtonDirective; index: number; } /** * @hidden */