Guest User

Untitled

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