Skip to content
Snippets Groups Projects
math-input.component.ts 4.19 KiB
Newer Older
rhenck's avatar
rhenck committed
import {
  AfterViewInit,
  Component,
  ElementRef,
  EventEmitter,
  Input,
  OnChanges, OnDestroy,
  Output,
  SimpleChanges,
  ViewChild
rhenck's avatar
rhenck committed
} from '@angular/core';
import { MathfieldElement } from '@iqb/mathlive';
import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { IQB_MATH_KEYBOARD_LAYOUTS } from 'common/math-editor/keyboard-layout.config';
rhenck's avatar
rhenck committed

@Component({
jojohoch's avatar
jojohoch committed
  selector: 'aspect-math-input',
rhenck's avatar
rhenck committed
  template: `
    <mat-button-toggle-group *ngIf="enableModeSwitch"
rhenck's avatar
rhenck committed
                             [value]="mathFieldElement.mode"
                             (change)="setParseMode($event)">
rhenck's avatar
rhenck committed
      <mat-button-toggle value="math">Formel</mat-button-toggle>
      <mat-button-toggle value="text">Text</mat-button-toggle>
    </mat-button-toggle-group>
jojohoch's avatar
jojohoch committed
    <div #inputRef
         (input)="onInput()"
         [class.full-width]="fullWidth"
         [class.inline-block]="!fullWidth"
         [class.read-only]="readonly"
         (focusin)="onFocusIn()"
         (focusout)="onFocusOut()">
rhenck's avatar
rhenck committed
    </div>
  `,
  styles: [`
    mat-button-toggle-group {
      height: auto;
    }
jojohoch's avatar
jojohoch committed
    :host ::ng-deep .full-width math-field {
      display: block;
    }

    .inline-block{
      display: inline-block;
    }

    :host ::ng-deep  math-field::part(virtual-keyboard-toggle) {
      display: none;
    }
    :host ::ng-deep math-field::part(menu-toggle) {
      display: none;
    }
    :host ::ng-deep .read-only math-field {
jojohoch's avatar
jojohoch committed
      outline: unset;
      border: unset;
    }
    :host ::ng-deep .mat-button-toggle-label-content {
      line-height: unset;
    }`
rhenck's avatar
rhenck committed
  ]
})
export class MathInputComponent implements AfterViewInit, OnChanges, OnDestroy {
rhenck's avatar
rhenck committed
  @Input() value!: string;
jojohoch's avatar
jojohoch committed
  @Input() fullWidth: boolean = true;
  @Input() readonly: boolean = false;
rhenck's avatar
rhenck committed
  @Input() enableModeSwitch: boolean = false;
jojohoch's avatar
jojohoch committed
  @Output() valueChange: EventEmitter<string> = new EventEmitter();
  @Output() focusIn: EventEmitter<MathfieldElement> = new EventEmitter();
  @Output() focusOut: EventEmitter<MathfieldElement> = new EventEmitter();
jojohoch's avatar
jojohoch committed
  @ViewChild('inputRef') inputRef!: ElementRef;

  protected readonly window = window;
jojohoch's avatar
jojohoch committed
  private readonly mathKeyboardLayout = IQB_MATH_KEYBOARD_LAYOUTS;
rhenck's avatar
rhenck committed

  mathFieldElement: MathfieldElement = new MathfieldElement({
    mathVirtualKeyboardPolicy: 'manual'
  constructor(public elementRef: ElementRef) {}
jojohoch's avatar
jojohoch committed

rhenck's avatar
rhenck committed
  ngAfterViewInit(): void {
    this.setupMathField();
    this.setKeyboardLayout();
jojohoch's avatar
jojohoch committed
    MathInputComponent.setupMathKeyboard();
  private setupMathField(): void {
jojohoch's avatar
jojohoch committed
    this.inputRef.nativeElement.appendChild(this.mathFieldElement);
rhenck's avatar
rhenck committed
    this.mathFieldElement.value = this.value;
    this.mathFieldElement.readOnly = this.readonly;
    setTimeout(() => {
      this.mathFieldElement.menuItems = [];
    }); // Disable context menu
jojohoch's avatar
jojohoch committed
  private static setupMathKeyboard(): void {
    window.mathVirtualKeyboard.addEventListener('virtual-keyboard-layer-change', () => MathInputComponent.resetShift());
  }

  private static resetShift(): void {
    window.mathVirtualKeyboard.shiftPressCount = 0;
rhenck's avatar
rhenck committed
  ngOnChanges(changes: SimpleChanges): void {
    if (changes.value) {
      this.mathFieldElement.setValue(changes.value.currentValue, { mode: 'text' });
    }
  // eslint-disable-next-line
jojohoch's avatar
jojohoch committed
  setFocus(offset?: number): void {
    this.mathFieldElement.focus();
  }

rhenck's avatar
rhenck committed
  setParseMode(event: MatButtonToggleChange) {
    this.mathFieldElement.mode = event.value;
jojohoch's avatar
jojohoch committed
    (this.inputRef.nativeElement.childNodes[0] as HTMLElement).focus();
rhenck's avatar
rhenck committed
  }
jojohoch's avatar
jojohoch committed
  onInput() {
    this.valueChange.emit(this.mathFieldElement.getValue());
  }

  onFocusIn() {
    this.focusIn.emit(this.mathFieldElement);
    window.mathVirtualKeyboard.show({ firstLayer: true, resetShift: true });
  }

  private setKeyboardLayout(): void {
    window.mathVirtualKeyboard.layouts = [
      this.mathKeyboardLayout.iqbNumeric,
      this.mathKeyboardLayout.iqbSymbols,
      this.mathKeyboardLayout.iqbText,
      this.mathKeyboardLayout.iqbGreek
    ];
  }

  onFocusOut() {
    this.focusOut.emit(this.mathFieldElement);
jojohoch's avatar
jojohoch committed
    window.mathVirtualKeyboard.hide();
  }

  // eslint-disable-next-line class-methods-use-this
  ngOnDestroy(): void {
    window.mathVirtualKeyboard
      .removeEventListener('virtual-keyboard-layer-change', () => MathInputComponent.resetShift());
  }
rhenck's avatar
rhenck committed
}