opf/openproject

View on GitHub
frontend/src/app/spot/components/toggle/toggle.component.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import {
  ChangeDetectorRef,
  ChangeDetectionStrategy,
  Component,
  EventEmitter,
  forwardRef,
  HostBinding,
  Input,
  Output,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

export interface SpotToggleOption<T> {
  value:T;
  title:string;
};

@Component({
  selector: 'spot-toggle',
  templateUrl: './toggle.component.html',
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => SpotToggleComponent),
    multi: true,
  }],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SpotToggleComponent<T> implements ControlValueAccessor {
  // TODO: These old styles will need to be replaced
  @HostBinding('class.form--field-inline-buttons-container') public classNameOld = true;

  @HostBinding('class.spot-toggle') public className = true;

  /**
   * The array of options that should be shown. Options have a simple structure:
   *
   * ```
   * {
   *   title:string;
   *   value:T;
   * }
   * ```
   *
   * `title` will be shown in the toggle for the user to read, while `value` will be the
   * value that is set as the ngModel if the option is selected.
   */
  @Input() options:SpotToggleOption<T>[] = [];

  /**
   * Whether the input should be disabled
   */
  @Input() disabled = false;

  /**
   * The name of the input. Will be autogenerated if not given,
   * but especially useful to provide in a hybrid Rails <-> Angular context
   * where a submit of a form is handled without JS.
   */
  @Input() name = `spot-toggle-${+(new Date())}`;

  /**
   * If you're not using Angular Reactive Forms (Which you should be using!)
   * then you can manually set the value via this input.
   */
  @Input() public value:T;

  /**
   * Emits when the selected value changes.
   */
  @Output() valueChange = new EventEmitter<T>();

  constructor(
    private cdRef:ChangeDetectorRef,
  ) {}

  writeValue(value:T):void {
    this.value = value;
    this.cdRef.markForCheck();
  }

  onToggle(value:T):void {
    this.writeValue(value);
    this.onChange(value);
    this.onTouched(value);
  }

  onChange = (_:T):void => {};

  onTouched: (t:T) => void = (_:T):void => {};

  registerOnChange(fn:(_:T) => void):void {
    this.onChange = fn;
  }

  registerOnTouched(fn:(_:T) => void):void {
    this.onTouched = fn;
  }
}