UNPKG

@engie-group/fluid-design-system-angular

Version:

Fluid Design System Angular

325 lines (283 loc) 6.55 kB
import { CommonModule } from '@angular/common'; import { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChildren, EventEmitter, forwardRef, Input, OnDestroy, Output, ViewEncapsulation } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Subject, takeUntil } from 'rxjs'; import { Utils } from '../../utils/utils.util'; import { IconComponent } from '../icon/icon.component'; import { RadioComponent } from '../radio/radio.component'; import { RadioGroupOrientation } from './radio-group.model'; @Component({ selector: 'nj-radio-group', templateUrl: './radio-group.component.html', providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RadioGroupComponent), multi: true } ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, standalone: true, imports: [RadioComponent, CommonModule, IconComponent] }) export class RadioGroupComponent implements ControlValueAccessor, AfterContentInit, OnDestroy { /** * @ignore */ private unsubscribe: Subject<void> = new Subject<void>(); /** * @ignore */ private _value; /** * @ignore */ private _isDisabled; /** * @ignore */ private _name; /** * @ignore */ private _required = false; /** * @ignore */ private _selected: RadioComponent; /** * @ignore */ private radioGroupClassName = 'nj-radio-group'; /** * Radio group selected value */ @Input() set value(newValue: any) { if (this._value !== newValue) { this._value = newValue; this._updateSelectedRadioFromValue(); this._checkSelectedRadioButton(); } } get value(): any { return this._value; } /** * Radio group selected radio component */ @Input() set selected(selected: RadioComponent) { this._selected = selected; this.value = selected ? selected.value : null; this._checkSelectedRadioButton(); } get selected() { return this._selected; } /** * 'Whether the radio group is disabled or not, this will force all the children radios be disabled or not depending on this value */ @Input() set isDisabled(value: boolean) { this._isDisabled = value; this._updateAllRadiosDisableValue(); this._markRadiosForCheck(); } get isDisabled(): boolean { return this._isDisabled; } /** * Radio group name, this will force all the children radios to have this name */ @Input() set name(value: string) { this._name = value; this._updateAllRadiosName(); this._markRadiosForCheck(); } get name(): string { return this._name; } /** * Whether radio is required or not */ @Input() set required(value: boolean) { this._required = value; this._markRadiosForCheck(); } get required(): boolean { return this._required; } /** * Whether the radio group should be displayed in column or row */ @Input() orientation: RadioGroupOrientation = 'column'; /** * Legend to label the radio group */ @Input() legend: string; /** * Message to provide when radio group is in error state */ @Input() errorMessage?: string; /** * Whether the input group is in error state */ @Input() hasError?: boolean; /** * Output that emits checked value on change only */ @Output() readonly valueChange: EventEmitter<string> = new EventEmitter<string>(); /** * All children radio components */ @ContentChildren(RadioComponent, { descendants: true }) radios; constructor(private cdr: ChangeDetectorRef) {} /** * @ignore */ private _onChange = (_: any): void => {}; /** * @ignore */ private _onTouched = (): void => {}; ngAfterContentInit() { this._listenForRadioChange(); this._updateSelectedRadioFromValue(); this._updateAllRadiosDisableValue(); this._updateAllRadiosName(); } ngOnDestroy() { this.unsubscribe.next(); this.unsubscribe.complete(); } /** * @ignore */ private _listenForRadioChange() { if (!this.radios) { return; } this.radios.forEach((radio) => { radio?.valueChange.pipe(takeUntil(this.unsubscribe)).subscribe((isSelected) => { if (isSelected) { this.value = radio?.value; this._onChange(this.value); this.valueChange.emit(this._value); } }); }); } /** * @ignore */ private _updateAllRadiosDisableValue() { if (!this.radios || Utils.isUndefinedOrNull(this.isDisabled)) { return; } this.radios.forEach((radio) => { if (radio) { radio.isDisabled = this.isDisabled; } }); } /** * @ignore */ private _updateAllRadiosName() { if (!this.radios || Utils.isUndefinedOrNull(this.name)) { return; } this.radios.forEach((radio) => { if (radio) { radio.name = this.name; } }); } /** * @ignore */ private _checkSelectedRadioButton() { if (this._selected && !this._selected.isChecked) { this._selected.isChecked = true; } } /** * @ignore */ private _markRadiosForCheck() { if (this.radios) { this.radios.forEach((radio) => radio?._markForCheck()); } } /** * @ignore */ private _updateSelectedRadioFromValue(): void { // If the value already matches the selected radio, do nothing. const isAlreadySelected = this._selected && this._selected.value === this._value; if (this.radios && !isAlreadySelected) { this._selected = null; this.radios.forEach((radio) => { if (!radio) { return; } radio.isChecked = this.value === radio.value; if (radio.isChecked) { this._selected = radio; } }); this._markRadiosForCheck(); } } /** * @ignore */ registerOnChange(fn: any): void { this._onChange = fn; } /** * @ignore */ registerOnTouched(fn: any): void { this._onTouched = fn; } /** * @ignore */ setDisabledState(isDisabled: boolean): void { this.isDisabled = isDisabled; this.cdr.markForCheck(); } /** * @ignore */ writeValue(value: any): void { this.value = value; this.cdr.markForCheck(); } /** * @ignore */ getOrientationClass(): string { if (this.orientation !== 'row') { return ''; } return `${this.radioGroupClassName}--${this.orientation}`; } }