UNPKG

@uiowa/uiowa-mfk-material

Version:

mfk, uiowa-mfk

570 lines (561 loc) 19.3 kB
import { InjectionToken, Injectable, ɵɵdefineInjectable, EventEmitter, Component, Input, Output, ViewChildren, NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { DigitOnlyDirective, DigitOnlyModule } from '@uiowa/digit-only'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { FlexLayoutModule } from '@angular/flex-layout'; import { MatFormFieldModule, MatInputModule } from '@angular/material'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const ConfigToken = new InjectionToken('config'); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Uiowa MFK Type * * class type contains 10 required MFK fields and 1 optional BFR field. * there are also several common methods ready to use. */ class Mfk { /** * Uiowa MFK. constructor takes a MFK string. * * Non-numeric characters will be stripped out automatically. * * Example usages: * ```typescript * let m1 = new Mfk(); * const m2 = new Mfk('010-11-1010-00000-00000000-6218-000-00000-00-1111'); * const m3 = new Mfk('0201210120100100000000621900000111123555'); * ``` * @param {?=} mfkString */ constructor(mfkString = null) { this.FUND = ''; this.ORG = ''; this.DEPT = ''; this.SUBDEPT = ''; this.GRANTPGM = ''; this.IACT = ''; this.OACT = ''; this.DACT = ''; this.FN = ''; this.CCTR = ''; this.BRF = ''; if (mfkString) { this.parseString(mfkString); } } /** * cast an object to type of MFK * * --> plain JSON object doesn't have type at run time. * @param {?} obj * @return {?} */ static cast(obj) { /** @type {?} */ const mfk = new Mfk(); Object.keys(obj).forEach((/** * @param {?} key * @return {?} */ key => (mfk[key.toUpperCase()] = obj[key]))); return mfk; } /** * Compare values of all 10 fields * * @param {?} mfk An MFK * @return {?} */ equals(mfk) { return (this.FUND === mfk.FUND && this.ORG === mfk.ORG && this.DEPT === mfk.DEPT && this.SUBDEPT === mfk.SUBDEPT && this.GRANTPGM === mfk.GRANTPGM && this.IACT === mfk.IACT && this.DACT === mfk.DACT && this.OACT === mfk.OACT && this.FN === mfk.FN && this.CCTR === mfk.CCTR); } /** * convert MFK to a 40 digit string. * @param {?=} separator (Optional) the separator between MFK parts. Default: "". * @return {?} MFK string */ to40String(separator = '') { return Object.keys(this) .filter((/** * @param {?} k * @return {?} */ (k) => k !== 'BRF')) .map((/** * @param {?} k * @return {?} */ k => this[k])) .join(separator); } /** * Get MFK format validation message. It checkes this MFK's first 10 fields are numeric strings or not. * @return {?} */ validateFormat() { /** @type {?} */ const mfkString = this.to40String(); if (mfkString.length !== 40) { return `MFK is not 40 digits long.`; } if (isNaN(Number(mfkString))) { return `MFK [${mfkString}] should be all numbers.`; } return null; } /** * check if this MFK is in an array of Favorite MFKs. * * @param {?} favoriteMfks an array of Favorite MFKs. * @return {?} boolean */ isIn(favoriteMfks) { if (!favoriteMfks || favoriteMfks.length < 1) { return false; } return favoriteMfks.some((/** * @param {?} x * @return {?} */ x => x.matches(this))); } /** * MFK module internal method. * @param {?} input * @return {?} */ parseString(input) { /** @type {?} */ const s = input.replace(/\D/g, ''); if (!s || s.length < 40) { return; } this.FUND = s.substring(0, 3); this.ORG = s.substring(3, 5); this.DEPT = s.substring(5, 9); this.SUBDEPT = s.substring(9, 14); this.GRANTPGM = s.substring(14, 22); this.IACT = s.substring(22, 26); this.OACT = s.substring(26, 29); this.DACT = s.substring(29, 34); this.FN = s.substring(34, 36); this.CCTR = s.substring(36, 40); if (s.length >= 42) { this.BRF = s.substring(40, 42); } } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class MfkFieldName { } MfkFieldName.FUND = 'FUND'; MfkFieldName.ORG = 'ORG'; MfkFieldName.DEPT = 'DEPT'; MfkFieldName.SUBDEPT = 'SUBDEPT'; MfkFieldName.GRANTPGM = 'GRANTPGM'; MfkFieldName.IACT = 'IACT'; MfkFieldName.OACT = 'OACT'; MfkFieldName.DACT = 'DACT'; MfkFieldName.FN = 'FN'; MfkFieldName.CCTR = 'CCTR'; MfkFieldName.BRF = 'BRF'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Options for MFK field. * * allows to set field default value, set readonly attribute, set validation regex pattern. Example usage: * * ```typescript * new MfkFieldOption(MfkFieldName.IACT, '6218') * new MfkFieldOption(MfkFieldName.IACT, '6218', true) * new MfkFieldOption(MfkFieldName.BRF) * ``` */ class MfkFieldOption { /** * Options for MFK field. * * allows to set field default value, set readonly attribute, set validation regex pattern. Example usage: * * ```typescript * new MfkFieldOption(MfkFieldName.IACT, '6218') * new MfkFieldOption(MfkFieldName.IACT, '6218', true) * new MfkFieldOption(MfkFieldName.BRF) * ``` * * @param {?} name (Required) the input field name. Use MfkFieldName type to get a proper value. * @param {?=} defaultValue (Optional) set a default value for this field. Default: ''. * @param {?=} readonly (Optional) set to true if the input field is readonly. Default: false. * @param {?=} valuePattern (Optional) set a regex for this field. Default: '^[0-9]+$'. */ constructor(name, defaultValue = '', readonly = false, valuePattern = '^[0-9]+$') { this.name = name; this.defaultValue = defaultValue; this.readonly = readonly; this.valuePattern = valuePattern; this.numericRegex = '^[0-9]+$'; this.getFieldLabelAndLength(); this.width = this.length * 0.65 + 0.75; if (!valuePattern) { valuePattern = this.numericRegex; } if (this.readonly) { if (!this.defaultValue) { throw new Error(`Default value for readonly field [${name}] is required.`); } return; // if readonly, then don't validate default value } if (defaultValue) { if (defaultValue.length !== this.length) { throw new Error(`The default value [${defaultValue}] for ${name} is not ${this.length} digits long.`); } /** @type {?} */ let reg = new RegExp(this.numericRegex); if (!reg.test(defaultValue)) { throw new Error(`The default value [${defaultValue}] for ${name} is not a number.`); } if (valuePattern !== this.numericRegex) { reg = new RegExp(valuePattern); if (!reg.test(defaultValue)) { throw new Error(`The default value [${defaultValue}] for ${name} doesn't match RegEx "${valuePattern}".`); } } } } /** * @private * @return {?} */ getFieldLabelAndLength() { switch (this.name) { case MfkFieldName.FUND: this.label = 'Fund'; this.length = 3; break; case MfkFieldName.ORG: this.label = 'Org'; this.length = 2; break; case MfkFieldName.DEPT: this.label = 'Dept'; this.length = 4; break; case MfkFieldName.SUBDEPT: this.label = 'Subdept'; this.length = 5; break; case MfkFieldName.GRANTPGM: this.label = `Grant/Pgm`; this.length = 8; break; case MfkFieldName.IACT: this.label = 'Iact'; this.length = 4; break; case MfkFieldName.OACT: this.label = 'Oact'; this.length = 3; break; case MfkFieldName.DACT: this.label = 'Dact'; this.length = 5; break; case MfkFieldName.FN: this.label = 'Fn'; this.length = 2; break; case MfkFieldName.CCTR: this.label = 'Cctr'; this.length = 4; break; case MfkFieldName.BRF: this.label = 'Brf'; this.length = 2; break; default: throw new Error(`MFK field name [${this.name}] is invalid.`); } } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class UiowaMfkOptionsService { constructor() { this.defaultOptions = [ new MfkFieldOption(MfkFieldName.FUND), new MfkFieldOption(MfkFieldName.ORG), new MfkFieldOption(MfkFieldName.DEPT), new MfkFieldOption(MfkFieldName.SUBDEPT), new MfkFieldOption(MfkFieldName.GRANTPGM), new MfkFieldOption(MfkFieldName.IACT), new MfkFieldOption(MfkFieldName.OACT), new MfkFieldOption(MfkFieldName.DACT), new MfkFieldOption(MfkFieldName.FN), new MfkFieldOption(MfkFieldName.CCTR) ]; } /** * @param {?=} options * @return {?} */ getOptions(options = []) { /** @type {?} */ const result = [...this.defaultOptions]; if (options && options.length > 0) { for (const option of options) { /** @type {?} */ const fieldOption = result.find((/** * @param {?} o * @return {?} */ o => o.name === option.name)); if (fieldOption) { /** @type {?} */ const index = result.indexOf(fieldOption); result[index] = option; } else { result.push(option); } } } return result; } /** * @return {?} */ getAllOptions() { return this.defaultOptions; } } UiowaMfkOptionsService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; /** @nocollapse */ UiowaMfkOptionsService.ngInjectableDef = ɵɵdefineInjectable({ factory: function UiowaMfkOptionsService_Factory() { return new UiowaMfkOptionsService(); }, token: UiowaMfkOptionsService, providedIn: "root" }); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class MfkInputComponent { /** * @param {?} optionsService */ constructor(optionsService) { this.optionsService = optionsService; this._mfk = new Mfk(); this.options = []; this.mfkChange = new EventEmitter(); } /** * @param {?} mfk * @return {?} */ set mfk(mfk) { this.options .filter((/** * @param {?} o * @return {?} */ o => o.defaultValue)) .forEach((/** * @param {?} o * @return {?} */ o => { if (!mfk[o.name]) { mfk[o.name] = o.defaultValue; } })); this._mfk = mfk; } /** * @return {?} */ get mfk() { this.options .filter((/** * @param {?} o * @return {?} */ o => o.readonly)) .forEach((/** * @param {?} o * @return {?} */ o => { this._mfk[o.name] = o.defaultValue; })); return this._mfk; } /** * @param {?} changes * @return {?} */ ngOnChanges(changes) { if (changes.mfk) { this.mfk = changes.mfk.currentValue; } if (changes.options) { this.options = changes.options.currentValue; this.options = this.optionsService.getOptions(this.options); } } /** * @return {?} */ ngOnInit() { this.options = this.optionsService.getAllOptions(); console.log(this.options); } /** * @return {?} */ ngAfterViewInit() { } /** * @param {?} e * @return {?} */ paste(e) { /** @type {?} */ const pastedInput = e.clipboardData.getData('text/plain').replace(/\D/g, ''); e.preventDefault(); if (!pastedInput) { return; } if (pastedInput.length < 40) { document.execCommand('insertText', false, pastedInput); } else { this.mfk.parseString(pastedInput); } this.mfkChange.emit(this.mfk); } /** * @param {?} e * @return {?} */ onKeyup(e) { this.mfkChange.emit(this.mfk); if ((e.keyCode < 48 || e.keyCode > 57) && (e.keyCode < 96 || e.keyCode > 105)) { return; // only numbers can trigger auto jump feature. } /** @type {?} */ const currentInputFieldName = e.target['name']; if (this.mfk[currentInputFieldName].length === e.target['maxLength']) { // auto jump to next input field when current field is full /** @type {?} */ const currentInputFieldIndex = this.options.findIndex((/** * @param {?} o * @return {?} */ o => o.name === currentInputFieldName)); for (let i = currentInputFieldIndex + 1; i < this.options.length; i++) { if (this.options[i].readonly) { continue; } /** @type {?} */ const nextInputField = this.mfkInputFields.find((/** * @param {?} v * @return {?} */ v => v.el.nativeElement['name'] === this.options[i].name)); nextInputField.el.nativeElement.focus(); break; } } } /** * @param {?} e * @return {?} */ onKeydown(e) { // handle "tab" key --> auto fill '0's if the input field has not completed if (e.keyCode !== 9) { return; } if (e.target['readOnly']) { return; } /** @type {?} */ const maxlength = e.target['maxLength']; while (this.mfk[e.target['name']].length < maxlength) { this.mfk[e.target['name']] = this.mfk[e.target['name']].concat('0'); } } } MfkInputComponent.decorators = [ { type: Component, args: [{ selector: 'uiowa-mfk-input', template: "<div id=\"mfk-container\">\r\n <div class=\"mfk-field\" fxLayout=\"row inline\" fxLayout.xs=\"column\" fxLayoutAlign=\"start center\"\r\n *ngFor=\"let option of options\" fxLayoutWrap fxLayoutGap=\"0.5%\">\r\n <mat-form-field class=\"form-control\" [ngStyle]=\"{'width.rem': option.width+0.5}\" appearance=\"outline\"\r\n floatLabel=\"always\" style=\" font-size: 14px;\">\r\n <mat-label>{{option.label}}</mat-label>\r\n <input matInput [attr.aria-label]=\"option.name\" [attr.name]=\"option.name\" [attr.maxlength]=\"option.length\"\r\n [readOnly]=\"option.readonly\" [(ngModel)]=\"mfk[option.name]\" (paste)=\"paste($event)\" (keyup)=\"onKeyup($event)\"\r\n (keydown)=\"onKeydown($event)\" inputmode=\"numeric\" pattern=\"[0-9]*\" digitOnly>\r\n </mat-form-field>\r\n\r\n\r\n </div>\r\n</div>\r\n", styles: [":host{display:inline-flex}.mfk-field{display:flex;flex-direction:row;flex-flow:row;vertical-align:bottom;margin-right:.25rem}.mat-form-field{padding:.375rem!important;font-size:1rem;font-weight:400;font-style:normal;font-variant:normal}.mat-label{font-size:.625rem;margin-bottom:0;white-space:nowrap}"] }] } ]; /** @nocollapse */ MfkInputComponent.ctorParameters = () => [ { type: UiowaMfkOptionsService } ]; MfkInputComponent.propDecorators = { mfk: [{ type: Input }], options: [{ type: Input }], mfkChange: [{ type: Output }], mfkInputFields: [{ type: ViewChildren, args: [DigitOnlyDirective,] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class UiowaMfkMaterialModule { /** * @param {?=} config * @return {?} */ static forRoot(config = { favoriteMfksApiUrl: 'api/myFavoriteMfks' }) { return { ngModule: UiowaMfkMaterialModule, providers: [ { provide: ConfigToken, useValue: config } ] }; } } UiowaMfkMaterialModule.decorators = [ { type: NgModule, args: [{ declarations: [MfkInputComponent], imports: [CommonModule, FormsModule, DigitOnlyModule, MatFormFieldModule, BrowserAnimationsModule, MatInputModule, FlexLayoutModule], exports: [MfkInputComponent] },] } ]; export { Mfk, MfkFieldName, MfkFieldOption, MfkInputComponent, UiowaMfkMaterialModule, UiowaMfkOptionsService as ɵa, ConfigToken as ɵb }; //# sourceMappingURL=uiowa-uiowa-mfk-material.js.map