@uiowa/uiowa-mfk-material
Version:
mfk, uiowa-mfk
570 lines (561 loc) • 19.3 kB
JavaScript
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