Advertisement
Guest User

Untitled

a guest
Mar 26th, 2019
155
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import NumericInput from "./NumericInput.js";
  2. import ParseMath from "./ParseMath.js";
  3.  
  4. export default class CalcInput extends NumericInput {
  5.   private spanElement: HTMLSpanElement;
  6.  
  7.   constructor(hostElementId: string) {
  8.     super(hostElementId);
  9.     this.spanElement = document.createElement("SPAN") as HTMLSpanElement;
  10.     this.hostElement.appendChild(this.spanElement);
  11.   }
  12.  
  13.   public destroy(): void {
  14.     super.destroy();
  15.     this.spanElement.remove();
  16.     this.spanElement = null;
  17.   }
  18.  
  19.   protected saveProps(
  20.     newValue: number,
  21.     sync: boolean = false,
  22.     originalText?: string
  23.   ) {
  24.     super.saveProps(newValue, sync, originalText);
  25.     this.spanElement.innerHTML = String(this.isValid ? this.value : "?");
  26.   }
  27.  
  28.   protected textToValue(text: string): number {
  29.     if (this.isEmptyString(text) || text === null) {
  30.       return null;
  31.     }
  32.     try {
  33.       const num: number = Number(ParseMath(text.trim()));
  34.       return isNaN(num) ? undefined : num;
  35.     } catch {
  36.       return undefined;
  37.     }
  38.   }
  39. }
  40.  
  41. import InputControl from "./InputControl.js";
  42.  
  43. export default class NumericInput extends InputControl<number> {
  44.   protected textToValue(text: string): number {
  45.     if (this.isEmptyString(text) || text === null) {
  46.       return null;
  47.     }
  48.     const num: number = Number(text.trim());
  49.     return isNaN(num) ? undefined : num;
  50.   }
  51.  
  52.   protected listener(): void {
  53.     this.saveText(this.inputElement.value);
  54.   }
  55.  
  56.   private saveText(text: string, sync: boolean = false): void {
  57.     this.saveProps(this.textToValue(text), sync, text);
  58.   }
  59.  
  60.   set value(newValue: number) {
  61.     this.saveProps(this.textToValue(String(newValue)), true);
  62.   }
  63.  
  64.   get value(): number {
  65.     return this._value;
  66.   }
  67.  
  68.   set text(newText: string) {
  69.     this.saveText(newText, true);
  70.   }
  71.  
  72.   get text(): string {
  73.     return this._text;
  74.   }
  75. }
  76.  
  77. import EventEmitter from "./EventEmitter.js";
  78.  
  79. export default class InputControl<T> {
  80.   public isValidChanged: EventEmitter = new EventEmitter();
  81.   public valueChanged: EventEmitter = new EventEmitter();
  82.   public textChanged: EventEmitter = new EventEmitter();
  83.   protected _text: string;
  84.   protected _value: any = null;
  85.   protected inputElement: HTMLInputElement;
  86.   protected hostElement: HTMLDivElement;
  87.   private _isValid: boolean;
  88.   private invalidCSSClass: string = "wij-invalid";
  89.   private baseCSSClass: string = "wij-control";
  90.  
  91.   constructor(hostElementId: string) {
  92.     this.hostElement = document.querySelector(`#${hostElementId}`);
  93.     const isInvalidElement: boolean =
  94.       !!this.hostElement &&
  95.       (this.hostElement.tagName !== "DIV" ||
  96.         !!this.hostElement.firstElementChild);
  97.     if (this.hostElement === null) {
  98.       console.error("Host element cannot be null.");
  99.     } else if (isInvalidElement) {
  100.       console.error("Host element must be an empty div.");
  101.     } else {
  102.       const input: HTMLInputElement = document.createElement(
  103.         "INPUT"
  104.       ) as HTMLInputElement;
  105.       input.setAttribute("type", "text");
  106.       input.addEventListener("input", this.listener.bind(this));
  107.       this.hostElement.appendChild(input);
  108.       this.inputElement = input;
  109.       this.inputElement.classList.add(this.baseCSSClass);
  110.     }
  111.   }
  112.  
  113.   public onValueChanged(): void {
  114.     this.valueChanged.broadcast(this.value);
  115.   }
  116.  
  117.   public onTextChanged(): void {
  118.     this.textChanged.broadcast(this.text);
  119.   }
  120.  
  121.   public onIsValidChanged(): void {
  122.     this.isValidChanged.broadcast(this.isValid);
  123.   }
  124.  
  125.   public destroy(): void {
  126.     this.hostElement.removeEventListener("input", this.listener);
  127.     this.inputElement.remove();
  128.     this.inputElement = null;
  129.     this.valueChanged.removeHandler();
  130.     this.isValidChanged.removeHandler();
  131.     this.textChanged.removeHandler();
  132.   }
  133.  
  134.   get isValid(): boolean {
  135.     return this._isValid;
  136.   }
  137.  
  138.   set value(newValue: T) {
  139.     this.saveProps(newValue, true);
  140.   }
  141.  
  142.   get value(): T {
  143.     return this._value;
  144.   }
  145.  
  146.   set text(newText: string) {
  147.     const oldValue: T = this.value;
  148.     this._text = newText;
  149.     this._value = this._text;
  150.     if (this.text !== newText) {
  151.       this.onTextChanged();
  152.     }
  153.     if (oldValue !== this.value) {
  154.       this.onValueChanged();
  155.     }
  156.   }
  157.  
  158.   get text(): string {
  159.     return this._text;
  160.   }
  161.  
  162.   protected listener(): void {}
  163.  
  164.   // pass originalText to save text w/o any changes
  165.   protected saveProps(
  166.     newValue: T,
  167.     sync: boolean = false,
  168.     originalText?: string
  169.   ) {
  170.     const oldValue: T = this.value;
  171.     const oldText: string = this.text;
  172.     let newText: string;
  173.     this._value = newValue;
  174.     if (this.value === null) {
  175.       newText = "";
  176.       this.setValid();
  177.     } else if (this.value === undefined) {
  178.       newText = null;
  179.       this.setInvalid();
  180.     } else {
  181.       newText = this.value.toString();
  182.       this.setValid();
  183.     }
  184.     this._text = originalText ? originalText : newText;
  185.     if (this.text !== oldText) {
  186.       this.onTextChanged();
  187.     }
  188.     if (this.value !== oldValue) {
  189.       this.onValueChanged();
  190.     }
  191.     if (sync) {
  192.       this.inputElement.value = this.text;
  193.     }
  194.   }
  195.  
  196.   // protected setter is required to call onIsValidChanged
  197.   // but different access modifiers is prohibited in ts
  198.   protected setValid(newIsValid: boolean = true): void {
  199.     const oldIsValid = this.isValid;
  200.     this._isValid = newIsValid;
  201.     if (this.isValid) {
  202.       this.inputElement.classList.remove(this.invalidCSSClass);
  203.     } else {
  204.       this.inputElement.classList.add(this.invalidCSSClass);
  205.     }
  206.     if (this.isValid !== oldIsValid) {
  207.       this.onIsValidChanged();
  208.     }
  209.   }
  210.  
  211.   protected setInvalid(): void {
  212.     this.setValid(false);
  213.   }
  214.  
  215.   protected isEmptyString = (str: string): boolean =>
  216.     String(str).trim().length === 0
  217. }
  218.  
  219. type Handler = (value: any) => void;
  220.  
  221. export default class EventEmitter {
  222.   private eventHandlers: Handler[] = [];
  223.  
  224.   public addHandler(eventHandler: Handler): void {
  225.     this.eventHandlers.push(eventHandler);
  226.   }
  227.  
  228.   public removeHandler(handler?: Handler): void {
  229.     if (!handler) {
  230.  
  231.       this.eventHandlers = [];
  232.     } else {
  233.       this.eventHandlers = this.eventHandlers.filter((eventHander: Handler) => handler !== eventHander);
  234.     }
  235.   }
  236.  
  237.   public broadcast(value): void {
  238.     this.eventHandlers.forEach((handler: Handler) => handler(value));
  239.   }
  240. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement