UNPKG

@nebular/theme

Version:
219 lines 7.82 kB
/* * @license * Copyright Akveo. All Rights Reserved. * Licensed under the MIT License. See License.txt in the project root for license information. */ import { ChangeDetectionStrategy, Component, ContentChildren, EventEmitter, forwardRef, Input, Output, QueryList, PLATFORM_ID, Inject, ElementRef, } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { from, fromEvent, merge, Subject } from 'rxjs'; import { filter, startWith, switchMap, takeUntil } from 'rxjs/operators'; import { convertToBoolProperty } from '../helpers'; import { NB_DOCUMENT } from '../../theme.options'; import { NbRadioComponent } from './radio.component'; /** * The `NbRadioGroupComponent` is the wrapper for `nb-radio` button. * It provides form bindings: * * ```html * <nb-radio-group [(ngModel)]="selectedOption"> * <nb-radio value="1">Option 1</nb-radio> * <nb-radio value="2">Option 2</nb-radio> * <nb-radio value="3">Option 3</nb-radio> * </nb-radio-group> * ``` * * Also, you can use `value` and `valueChange` for binding without forms. * * ```html * <nb-radio-group [(value)]="selectedOption"> * <nb-radio value="1">Option 1</nb-radio> * <nb-radio value="2">Option 2</nb-radio> * <nb-radio value="3">Option 3</nb-radio> * </nb-radio-group> * ``` * * Radio items name has to be provided through `name` input property of the radio group. * * ```html * <nb-radio-group name="my-radio-group"> * ... * </nb-radio-group> * ``` * * You can change radio group status by setting `status` input. * @stacked-example(Statuses, radio/radio-statuses.component) * * Also, you can disable the whole group using `disabled` attribute. * @stacked-example(Disabled group, radio/radio-disabled-group.component) * * */ export class NbRadioGroupComponent { constructor(hostElement, platformId, document) { this.hostElement = hostElement; this.platformId = platformId; this.document = document; this.destroy$ = new Subject(); this.onChange = (value) => { }; this.onTouched = () => { }; this._status = 'basic'; this.valueChange = new EventEmitter(); } get value() { return this._value; } set value(value) { this._value = value; this.updateValues(); } get name() { return this._name; } set name(name) { this._name = name; this.updateNames(); } get disabled() { return this._disabled; } set disabled(disabled) { this._disabled = convertToBoolProperty(disabled); this.updateDisabled(); } /** * Radio buttons status. * Possible values are `primary` (default), `success`, `warning`, `danger`, `info`. */ get status() { return this._status; } set status(value) { if (this._status !== value) { this._status = value; this.updateStatus(); } } ngAfterContentInit() { // In case option 'name' isn't set on nb-radio component, // we need to set it's name right away, so it won't overlap with options // without names from other radio groups. Otherwise they all would have // same name and will be considered as options from one group so only the // last option will stay selected. this.updateNames(); this.radios.changes .pipe(startWith(this.radios), // 'changes' emit during change detection run and we can't update // option properties right of since they already was initialized. // Instead we schedule microtask to update radios after change detection // run is finished and trigger one more change detection run. switchMap((radios) => from(Promise.resolve(radios))), takeUntil(this.destroy$)) .subscribe(() => this.updateAndSubscribeToRadios()); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } registerOnChange(fn) { this.onChange = fn; } registerOnTouched(fn) { this.onTouched = fn; } writeValue(value) { this.value = value; } updateAndSubscribeToRadios() { this.updateValueFromCheckedOption(); this.updateNames(); this.updateValues(); this.updateDisabled(); this.updateStatus(); this.subscribeOnRadiosValueChange(); this.subscribeOnRadiosBlur(); } updateNames() { if (this.radios) { this.radios.forEach((radio) => radio._setName(this.name)); } } updateValues() { this.updateAndMarkForCheckRadios((radio) => radio.checked = radio.value === this.value); } updateDisabled() { if (typeof this.disabled !== 'undefined') { this.updateAndMarkForCheckRadios((radio) => radio.disabled = this.disabled); } } subscribeOnRadiosValueChange() { if (!this.radios || !this.radios.length) { return; } merge(...this.radios.map((radio) => radio.valueChange)) .pipe(takeUntil(merge(this.radios.changes, this.destroy$))) .subscribe((value) => { this.writeValue(value); this.propagateValue(value); }); } propagateValue(value) { this.valueChange.emit(value); this.onChange(value); } subscribeOnRadiosBlur() { const hasNoRadios = !this.radios || !this.radios.length; if (!isPlatformBrowser(this.platformId) || hasNoRadios) { return; } const hostElement = this.hostElement.nativeElement; fromEvent(hostElement, 'focusin') .pipe(filter(event => hostElement.contains(event.target)), switchMap(() => merge(fromEvent(this.document, 'focusin'), fromEvent(this.document, 'click'))), filter(event => !hostElement.contains(event.target)), takeUntil(merge(this.radios.changes, this.destroy$))) .subscribe(() => this.onTouched()); } updateStatus() { this.updateAndMarkForCheckRadios((radio) => radio.status = this.status); } updateAndMarkForCheckRadios(updateFn) { if (this.radios) { this.radios.forEach((radio) => { updateFn(radio); radio._markForCheck(); }); } } updateValueFromCheckedOption() { const checkedRadio = this.radios.find((radio) => radio.checked); const isValueMissing = this.value === undefined || this.value === null; if (checkedRadio && isValueMissing && checkedRadio.value !== this.value) { this.value = checkedRadio.value; } } } NbRadioGroupComponent.decorators = [ { type: Component, args: [{ selector: 'nb-radio-group', template: ` <ng-content select="nb-radio"></ng-content>`, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NbRadioGroupComponent), multi: true, }, ], changeDetection: ChangeDetectionStrategy.OnPush },] } ]; NbRadioGroupComponent.ctorParameters = () => [ { type: ElementRef }, { type: undefined, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] }, { type: undefined, decorators: [{ type: Inject, args: [NB_DOCUMENT,] }] } ]; NbRadioGroupComponent.propDecorators = { value: [{ type: Input }], name: [{ type: Input }], disabled: [{ type: Input }], status: [{ type: Input }], radios: [{ type: ContentChildren, args: [NbRadioComponent, { descendants: true },] }], valueChange: [{ type: Output }] }; //# sourceMappingURL=radio-group.component.js.map