@engie-group/fluid-design-system-angular
Version:
Fluid Design System Angular
325 lines (283 loc) • 6.55 kB
text/typescript
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';
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
*/
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
*/
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
*/
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
*/
set name(value: string) {
this._name = value;
this._updateAllRadiosName();
this._markRadiosForCheck();
}
get name(): string {
return this._name;
}
/**
* Whether radio is required or not
*/
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
*/
orientation: RadioGroupOrientation = 'column';
/**
* Legend to label the radio group
*/
legend: string;
/**
* Message to provide when radio group is in error state
*/
errorMessage?: string;
/**
* Whether the input group is in error state
*/
hasError?: boolean;
/**
* Output that emits checked value on change only
*/
readonly valueChange: EventEmitter<string> = new EventEmitter<string>();
/**
* All children radio components
*/
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}`;
}
}