@onemrvapublic/design-system 16.2.8 → 16.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/projects/onemrva/design-system/_index.scss +1 -0
- package/projects/onemrva/design-system/core/index.ts +2 -0
- package/projects/onemrva/design-system/core/ng-package.json +6 -0
- package/projects/onemrva/design-system/core/src/lib/core.module.ts +91 -0
- package/projects/onemrva/design-system/core/src/lib/modules/index.ts +1 -0
- package/projects/onemrva/design-system/core/src/lib/modules/translate.loader.ts +34 -0
- package/projects/onemrva/design-system/core/src/lib/modules/translate.module.ts +66 -0
- package/projects/onemrva/design-system/core/src/lib/services/index.ts +2 -0
- package/projects/onemrva/design-system/core/src/lib/services/onemrva-error-handler.service.ts +24 -0
- package/projects/onemrva/design-system/core/src/lib/services/onemrva-missing-translation.service.ts +12 -0
- package/projects/onemrva/design-system/core/src/test.ts +24 -0
- package/projects/onemrva/design-system/package.json +1 -1
- package/projects/onemrva/design-system/shared/index.ts +10 -0
- package/projects/onemrva/design-system/shared/ng-package.json +6 -0
- package/projects/onemrva/design-system/shared/src/lib/components/clipboard-icon/clipboard-icon.component.css +0 -0
- package/projects/onemrva/design-system/shared/src/lib/components/clipboard-icon/clipboard-icon.component.html +1 -0
- package/projects/onemrva/design-system/shared/src/lib/components/clipboard-icon/clipboard-icon.component.spec.ts +21 -0
- package/projects/onemrva/design-system/shared/src/lib/components/clipboard-icon/clipboard-icon.component.ts +22 -0
- package/projects/onemrva/design-system/shared/src/lib/directives/clipboard.directive.ts +55 -0
- package/projects/onemrva/design-system/shared/src/lib/directives/color.directive.ts +47 -0
- package/projects/onemrva/design-system/shared/src/lib/directives/digit-only.directive.ts +46 -0
- package/projects/onemrva/design-system/shared/src/lib/directives/if-width-is.directive.ts +27 -0
- package/projects/onemrva/design-system/shared/src/lib/directives/index.ts +22 -0
- package/projects/onemrva/design-system/shared/src/lib/directives/mask.directive.ts +221 -0
- package/projects/onemrva/design-system/shared/src/lib/directives/mat-row-clickable.directive.ts +58 -0
- package/projects/onemrva/design-system/shared/src/lib/pipes/index.ts +5 -0
- package/projects/onemrva/design-system/shared/src/lib/pipes/onemrva-bce.pipe.ts +18 -0
- package/projects/onemrva/design-system/shared/src/lib/pipes/onemrva-niss.pipe.ts +18 -0
- package/projects/onemrva/design-system/shared/src/lib/shared.module.ts +10 -0
- package/projects/onemrva/design-system/shared/src/lib/validators/onemrva-validators.ts +66 -0
package/package.json
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
@forward "./mat-breadcrumb/src/onemrva-mat-breadcrumb.component" show breadcrumb;
|
|
8
8
|
@forward "./mat-datepicker-header/src/onemrva-mat-datepicker-header.component"
|
|
9
9
|
show datepickerHeader;
|
|
10
|
+
@forward "./layout/src/components/layout/layout-mixin.component" show layout;
|
|
10
11
|
@forward "./mat-message-box/src/onemrva-mat-message-box.component" show
|
|
11
12
|
messageBox;
|
|
12
13
|
@forward "./mat-multi-select/src/onemrva-mat-multi-select.component" show
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
import { MatExpansionModule } from '@angular/material/expansion';
|
|
4
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
5
|
+
import {
|
|
6
|
+
MatSnackBarModule,
|
|
7
|
+
MAT_SNACK_BAR_DEFAULT_OPTIONS,
|
|
8
|
+
} from '@angular/material/snack-bar';
|
|
9
|
+
import { OnemrvaErrorHandler } from './services';
|
|
10
|
+
import { CommonModule } from '@angular/common';
|
|
11
|
+
import {
|
|
12
|
+
DateAdapter,
|
|
13
|
+
MAT_DATE_FORMATS,
|
|
14
|
+
MAT_DATE_LOCALE,
|
|
15
|
+
MatNativeDateModule, //
|
|
16
|
+
} from '@angular/material/core';
|
|
17
|
+
import {
|
|
18
|
+
MAT_MOMENT_DATE_ADAPTER_OPTIONS,
|
|
19
|
+
MatMomentDateModule, //
|
|
20
|
+
MomentDateAdapter,
|
|
21
|
+
} from '@angular/material-moment-adapter';
|
|
22
|
+
|
|
23
|
+
import { TranslateService } from '@ngx-translate/core';
|
|
24
|
+
import { Observable, of } from 'rxjs';
|
|
25
|
+
|
|
26
|
+
function translateDatepicker(
|
|
27
|
+
translateService: TranslateService,
|
|
28
|
+
adapter: DateAdapter<Date>
|
|
29
|
+
): () => Observable<any> {
|
|
30
|
+
return () => {
|
|
31
|
+
// For some cosmic reason, return translateService.onLangChange().asObservable()) + pipe(tap()) does not work
|
|
32
|
+
translateService.onLangChange.subscribe((event) => {
|
|
33
|
+
adapter.setLocale(event.lang);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// But this works fine...
|
|
37
|
+
return of('sin (a +- b) = sin a . cos b +- cos a . sin b');
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const MY_FORMATS = {
|
|
42
|
+
parse: {
|
|
43
|
+
dateInput: 'LL',
|
|
44
|
+
},
|
|
45
|
+
display: {
|
|
46
|
+
dateInput: 'DD/MM/YYYY',
|
|
47
|
+
monthYearLabel: 'MMM YYYY',
|
|
48
|
+
dateA11yLabel: 'LL',
|
|
49
|
+
monthYearA11yLabel: 'MMMM YYYY',
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
@NgModule({
|
|
54
|
+
imports: [
|
|
55
|
+
CommonModule,
|
|
56
|
+
MatExpansionModule,
|
|
57
|
+
MatIconModule,
|
|
58
|
+
MatSnackBarModule,
|
|
59
|
+
MatNativeDateModule,
|
|
60
|
+
MatMomentDateModule,
|
|
61
|
+
],
|
|
62
|
+
exports: [MatSnackBarModule],
|
|
63
|
+
providers: [
|
|
64
|
+
{
|
|
65
|
+
provide: ErrorHandler,
|
|
66
|
+
useClass: OnemrvaErrorHandler,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
provide: MAT_SNACK_BAR_DEFAULT_OPTIONS,
|
|
70
|
+
useValue: { horizontalPosition: 'right' },
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
provide: MAT_DATE_LOCALE,
|
|
74
|
+
useValue: 'fr',
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
{
|
|
78
|
+
provide: DateAdapter,
|
|
79
|
+
useClass: MomentDateAdapter,
|
|
80
|
+
deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS],
|
|
81
|
+
},
|
|
82
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS },
|
|
83
|
+
{
|
|
84
|
+
provide: APP_INITIALIZER,
|
|
85
|
+
useFactory: translateDatepicker,
|
|
86
|
+
deps: [TranslateService, DateAdapter<any>],
|
|
87
|
+
multi: true,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
})
|
|
91
|
+
export class OnemrvaCoreModule {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./translate.module";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// import {Injectable} from "@angular/core";
|
|
2
|
+
// import {TranslateLoader} from "@ngx-translate/core";
|
|
3
|
+
// import {HttpClient} from "@angular/common/http";
|
|
4
|
+
// import {forkJoin, Observable, of} from "rxjs";
|
|
5
|
+
// import {catchError, map} from "rxjs/operators";
|
|
6
|
+
//
|
|
7
|
+
// @Injectable()
|
|
8
|
+
// export class OnemrvaTranslateHttpLoader implements TranslateLoader {
|
|
9
|
+
//
|
|
10
|
+
// constructor(
|
|
11
|
+
// private http: HttpClient,
|
|
12
|
+
// ) {
|
|
13
|
+
//
|
|
14
|
+
// }
|
|
15
|
+
//
|
|
16
|
+
// /**
|
|
17
|
+
// * Gets the translations from the server
|
|
18
|
+
// */
|
|
19
|
+
// public getTranslation(lang: string): Observable<Object> {
|
|
20
|
+
//
|
|
21
|
+
// const observables = [
|
|
22
|
+
// this.http.get(`${environment.translate.prefix}${lang}${environment.translate.suffix}`).pipe(catchError(() => of(null))),
|
|
23
|
+
// ...environment.translate.modules.map((m) =>
|
|
24
|
+
// this.http.get(`${environment.translate.prefix}${m}/${lang}${environment.translate.suffix}`).pipe(catchError(() => of(null)))
|
|
25
|
+
// ),
|
|
26
|
+
// ];
|
|
27
|
+
//
|
|
28
|
+
// return forkJoin(observables).pipe(
|
|
29
|
+
// map((all) => {
|
|
30
|
+
// return (all.filter((v) => !!v) as Object[]).reduce((s, c) => ({ ...s, ...c }), {});
|
|
31
|
+
// })
|
|
32
|
+
// );
|
|
33
|
+
// }
|
|
34
|
+
// }
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// import { HttpClient } from '@angular/common/http';
|
|
2
|
+
// import {APP_INITIALIZER, NgModule} from '@angular/core';
|
|
3
|
+
// import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
|
|
4
|
+
// import { environment } from "@env/environment";
|
|
5
|
+
// import {OnemrvaTranslateHttpLoader} from "./translate.loader";
|
|
6
|
+
//
|
|
7
|
+
// @NgModule({
|
|
8
|
+
// imports: [
|
|
9
|
+
// TranslateModule.forRoot({
|
|
10
|
+
// defaultLanguage: environment.translate.language,
|
|
11
|
+
// loader: {
|
|
12
|
+
// provide: TranslateLoader,
|
|
13
|
+
// useFactory: (http: HttpClient) => {
|
|
14
|
+
// return new OnemrvaTranslateHttpLoader(http);
|
|
15
|
+
// },
|
|
16
|
+
// deps: [HttpClient],
|
|
17
|
+
// },
|
|
18
|
+
// }),
|
|
19
|
+
// ],
|
|
20
|
+
// providers: [
|
|
21
|
+
// {
|
|
22
|
+
// provide: APP_INITIALIZER,
|
|
23
|
+
// deps: [TranslateService],
|
|
24
|
+
// useFactory: (translateService: TranslateService ) => {
|
|
25
|
+
// return async () => {
|
|
26
|
+
// const languages = environment.translate.languages.map(v => v.code);
|
|
27
|
+
// translateService.addLangs(languages);
|
|
28
|
+
// translateService.setDefaultLang(environment.translate.language);
|
|
29
|
+
//
|
|
30
|
+
// // getting language from localstorage
|
|
31
|
+
// let language:string|null = localStorage.getItem('language');
|
|
32
|
+
// // not set or incorrectly set ?
|
|
33
|
+
// if (language === null || languages.indexOf(language) < 0) {
|
|
34
|
+
// // getting language from browser
|
|
35
|
+
// if (languages.indexOf(navigator.language) >= 0 ) {
|
|
36
|
+
// language = navigator.language;
|
|
37
|
+
// } else {
|
|
38
|
+
// // getting language from one of the browser's languages
|
|
39
|
+
// for (let lng of navigator.languages) {
|
|
40
|
+
// if (languages.indexOf(lng) >= 0 ) {
|
|
41
|
+
// language = lng;
|
|
42
|
+
// break;
|
|
43
|
+
// }
|
|
44
|
+
// }
|
|
45
|
+
// // getting default language
|
|
46
|
+
// if (language === null || languages.indexOf(language) < 0) {
|
|
47
|
+
// language = environment.translate.language;
|
|
48
|
+
// }
|
|
49
|
+
// }
|
|
50
|
+
// }
|
|
51
|
+
// localStorage.setItem('language', language);
|
|
52
|
+
// await translateService.use(language).toPromise();
|
|
53
|
+
//
|
|
54
|
+
// translateService.onLangChange.subscribe((value) => {
|
|
55
|
+
// localStorage.setItem('language', value.lang);
|
|
56
|
+
// });
|
|
57
|
+
// };
|
|
58
|
+
// },
|
|
59
|
+
// multi: true,
|
|
60
|
+
// }
|
|
61
|
+
// ],
|
|
62
|
+
// exports: [
|
|
63
|
+
// TranslateModule
|
|
64
|
+
// ]
|
|
65
|
+
// })
|
|
66
|
+
// export class OnemrvaTranslateModule { }
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ErrorHandler, Injectable } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
MatSnackBar,
|
|
4
|
+
MatSnackBarHorizontalPosition,
|
|
5
|
+
MatSnackBarVerticalPosition,
|
|
6
|
+
} from '@angular/material/snack-bar';
|
|
7
|
+
|
|
8
|
+
@Injectable()
|
|
9
|
+
export class OnemrvaErrorHandler implements ErrorHandler {
|
|
10
|
+
constructor(private _snackBar: MatSnackBar) {}
|
|
11
|
+
horizontalPosition: MatSnackBarHorizontalPosition = 'end';
|
|
12
|
+
verticalPosition: MatSnackBarVerticalPosition = 'bottom';
|
|
13
|
+
|
|
14
|
+
handleError(error: any) {
|
|
15
|
+
console.error(error);
|
|
16
|
+
//alert("Error: " + error.message);
|
|
17
|
+
this._snackBar.open(`Error: ${error.message}`, '', {
|
|
18
|
+
duration: 5000,
|
|
19
|
+
panelClass: 'mat-primary',
|
|
20
|
+
horizontalPosition: this.horizontalPosition,
|
|
21
|
+
verticalPosition: this.verticalPosition,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
package/projects/onemrva/design-system/core/src/lib/services/onemrva-missing-translation.service.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MissingTranslationHandler,
|
|
3
|
+
MissingTranslationHandlerParams,
|
|
4
|
+
} from '@ngx-translate/core';
|
|
5
|
+
|
|
6
|
+
export class OnemrvaMissingTranslationHandler
|
|
7
|
+
implements MissingTranslationHandler
|
|
8
|
+
{
|
|
9
|
+
handle(params: MissingTranslationHandlerParams) {
|
|
10
|
+
return `???${params.key}???`;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
|
2
|
+
|
|
3
|
+
import 'zone.js';
|
|
4
|
+
import 'zone.js/testing';
|
|
5
|
+
import { getTestBed } from '@angular/core/testing';
|
|
6
|
+
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
|
|
7
|
+
|
|
8
|
+
declare const require: {
|
|
9
|
+
context(
|
|
10
|
+
path: string,
|
|
11
|
+
deep?: boolean,
|
|
12
|
+
filter?: RegExp
|
|
13
|
+
): {
|
|
14
|
+
keys(): string[];
|
|
15
|
+
<T>(id: string): T;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// First, initialize the Angular testing environment.
|
|
20
|
+
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
|
|
21
|
+
// Then we find all the tests.
|
|
22
|
+
const context = require.context('./', true, /\.spec\.ts$/);
|
|
23
|
+
// And load the modules.
|
|
24
|
+
context.keys().map(context);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Public API Surface of shared
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './src/lib/directives';
|
|
6
|
+
export * from './src/lib/shared.module';
|
|
7
|
+
export * from './src/lib/pipes';
|
|
8
|
+
export * from './src/lib/validators/onemrva-validators';
|
|
9
|
+
|
|
10
|
+
export const NISS_MASK = '000000/000-00';
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<mat-icon [matTooltip]="'TEST'">content_copy</mat-icon>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { ClipboardIconComponent } from './clipboard-icon.component';
|
|
4
|
+
|
|
5
|
+
describe('ClipboardIconComponent', () => {
|
|
6
|
+
let component: ClipboardIconComponent;
|
|
7
|
+
let fixture: ComponentFixture<ClipboardIconComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
TestBed.configureTestingModule({
|
|
11
|
+
declarations: [ClipboardIconComponent]
|
|
12
|
+
});
|
|
13
|
+
fixture = TestBed.createComponent(ClipboardIconComponent);
|
|
14
|
+
component = fixture.componentInstance;
|
|
15
|
+
fixture.detectChanges();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should create', () => {
|
|
19
|
+
expect(component).toBeTruthy();
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {Component, ElementRef, ViewChild} from '@angular/core';
|
|
2
|
+
import {MatIconModule} from "@angular/material/icon";
|
|
3
|
+
import {MatTooltipModule} from "@angular/material/tooltip";
|
|
4
|
+
import {Clipboard} from "@angular/cdk/clipboard";
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'lib-clipboard-icon',
|
|
8
|
+
templateUrl: './clipboard-icon.component.html',
|
|
9
|
+
styleUrls: ['./clipboard-icon.component.css'],
|
|
10
|
+
standalone: true,
|
|
11
|
+
imports: [
|
|
12
|
+
MatIconModule,
|
|
13
|
+
MatTooltipModule
|
|
14
|
+
]
|
|
15
|
+
})
|
|
16
|
+
export class ClipboardIconComponent {
|
|
17
|
+
constructor(
|
|
18
|
+
public _elementRef : ElementRef,
|
|
19
|
+
private clipboardService : Clipboard) {
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChangeDetectorRef,
|
|
3
|
+
ComponentFactoryResolver, ComponentRef,
|
|
4
|
+
Directive,
|
|
5
|
+
ElementRef,
|
|
6
|
+
HostBinding,
|
|
7
|
+
Input,
|
|
8
|
+
OnInit,
|
|
9
|
+
Renderer2,
|
|
10
|
+
ViewContainerRef
|
|
11
|
+
} from '@angular/core';
|
|
12
|
+
import {Clipboard} from "@angular/cdk/clipboard";
|
|
13
|
+
import {ClipboardIconComponent} from "../components/clipboard-icon/clipboard-icon.component";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Conditionally adds component to the tree if screen width matches at least one size in input
|
|
17
|
+
*/
|
|
18
|
+
@Directive({
|
|
19
|
+
selector: 'span[clipboard]',
|
|
20
|
+
host: {
|
|
21
|
+
// '[style.padding]': '"pointer"',
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
})
|
|
25
|
+
export class OnemRvaClipboardDirective implements OnInit{
|
|
26
|
+
|
|
27
|
+
private icon: ComponentRef<ClipboardIconComponent>;
|
|
28
|
+
private iconEl: any;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
private elementRef: ElementRef,
|
|
32
|
+
private renderer: Renderer2,
|
|
33
|
+
private factory: ComponentFactoryResolver,
|
|
34
|
+
private vcRef: ViewContainerRef,
|
|
35
|
+
private clipboardService : Clipboard) {
|
|
36
|
+
|
|
37
|
+
const miFactory = this.factory.resolveComponentFactory(ClipboardIconComponent);
|
|
38
|
+
this.icon = this.vcRef.createComponent(miFactory);
|
|
39
|
+
this.iconEl = this.icon.injector.get(ClipboardIconComponent)._elementRef.nativeElement;
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@HostBinding('class')
|
|
44
|
+
class = "";
|
|
45
|
+
|
|
46
|
+
@Input()
|
|
47
|
+
clipboard: string = '';
|
|
48
|
+
|
|
49
|
+
ngOnInit(): void {
|
|
50
|
+
this.vcRef.clear();
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
this.renderer.appendChild(this.elementRef.nativeElement, this.iconEl);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {Directive, HostBinding, Input} from '@angular/core';
|
|
2
|
+
import {OnemrvaMatColor} from "@onemrvapublic/design-system/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Conditionally adds component to the tree if screen width matches at least one size in input
|
|
6
|
+
*/
|
|
7
|
+
@Directive({
|
|
8
|
+
selector: 'mat-card[color],mat-chip[color]',
|
|
9
|
+
})
|
|
10
|
+
export class OnemRvaColorDirective {
|
|
11
|
+
|
|
12
|
+
constructor() {}
|
|
13
|
+
|
|
14
|
+
@Input()
|
|
15
|
+
color: OnemrvaMatColor = '';
|
|
16
|
+
|
|
17
|
+
/** @hidden @internal */
|
|
18
|
+
@HostBinding('class.mat-primary')
|
|
19
|
+
public get _isPrimary(): boolean {
|
|
20
|
+
return this.color === OnemrvaMatColor.PRIMARY;
|
|
21
|
+
}
|
|
22
|
+
/** @hidden @internal */
|
|
23
|
+
@HostBinding('class.mat-accent')
|
|
24
|
+
public get _isAccent(): boolean {
|
|
25
|
+
return this.color === OnemrvaMatColor.ACCENT;
|
|
26
|
+
}
|
|
27
|
+
/** @hidden @internal */
|
|
28
|
+
@HostBinding('class.mat-error')
|
|
29
|
+
public get _isError(): boolean {
|
|
30
|
+
return this.color === OnemrvaMatColor.ERROR;
|
|
31
|
+
}
|
|
32
|
+
/** @hidden @internal */
|
|
33
|
+
@HostBinding('class.mat-warn')
|
|
34
|
+
public get _isWarn(): boolean {
|
|
35
|
+
return this.color === OnemrvaMatColor.WARN;
|
|
36
|
+
}
|
|
37
|
+
/** @hidden @internal */
|
|
38
|
+
@HostBinding('class.mat-success')
|
|
39
|
+
public get _isSuccess(): boolean {
|
|
40
|
+
return this.color === OnemrvaMatColor.SUCCESS;
|
|
41
|
+
}
|
|
42
|
+
/** @hidden @internal */
|
|
43
|
+
@HostBinding('class.mat-info')
|
|
44
|
+
public get _isInfo(): boolean {
|
|
45
|
+
return this.color === OnemrvaMatColor.INFO;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Directive, HostListener } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Directive({
|
|
4
|
+
selector: '[digitOnly]',
|
|
5
|
+
})
|
|
6
|
+
export class DigitOnlyDirective {
|
|
7
|
+
constructor() {}
|
|
8
|
+
|
|
9
|
+
@HostListener('keydown', ['$event']) onKeyDown(event: any) {
|
|
10
|
+
let e = <KeyboardEvent>event;
|
|
11
|
+
|
|
12
|
+
const allowedKey = [
|
|
13
|
+
'0',
|
|
14
|
+
'1',
|
|
15
|
+
'2',
|
|
16
|
+
'3',
|
|
17
|
+
'4',
|
|
18
|
+
'5',
|
|
19
|
+
'6',
|
|
20
|
+
'7',
|
|
21
|
+
'8',
|
|
22
|
+
'9',
|
|
23
|
+
'.',
|
|
24
|
+
',',
|
|
25
|
+
' ',
|
|
26
|
+
'Backspace',
|
|
27
|
+
'Delete',
|
|
28
|
+
'Tab',
|
|
29
|
+
'-',
|
|
30
|
+
'Enter',
|
|
31
|
+
'ArrowLeft',
|
|
32
|
+
'ArrowRight',
|
|
33
|
+
'ArrowUp',
|
|
34
|
+
'ArrowDown',
|
|
35
|
+
'Home',
|
|
36
|
+
'End',
|
|
37
|
+
];
|
|
38
|
+
if (allowedKey.indexOf(e.key) >= 0) return;
|
|
39
|
+
|
|
40
|
+
// HWKTODO manage ctrl Z and ctrl U
|
|
41
|
+
const allowedCtrlShortcuts = ['a', 'c', 'x', 'v'];
|
|
42
|
+
if (e.ctrlKey && allowedCtrlShortcuts.indexOf(e.key) >= 0) return;
|
|
43
|
+
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout';
|
|
2
|
+
import { Directive, Input, OnInit, TemplateRef, ViewContainerRef } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Conditionally adds component to the tree if screen width matches at least one size in input
|
|
6
|
+
*/
|
|
7
|
+
@Directive({
|
|
8
|
+
selector: '[ifWidthIs]',
|
|
9
|
+
})
|
|
10
|
+
export class IfWidthIsDirective implements OnInit {
|
|
11
|
+
@Input() ifWidthIs!: typeof Breakpoints[keyof typeof Breakpoints][];
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
public breakpointObserver: BreakpointObserver,
|
|
15
|
+
private _templateRef: TemplateRef<any>,
|
|
16
|
+
private _viewContainer: ViewContainerRef
|
|
17
|
+
) {}
|
|
18
|
+
|
|
19
|
+
ngOnInit() {
|
|
20
|
+
this.breakpointObserver.observe([...this.ifWidthIs]).subscribe((state: BreakpointState) => {
|
|
21
|
+
this._viewContainer.clear();
|
|
22
|
+
if (state.matches) {
|
|
23
|
+
this._viewContainer.createEmbeddedView(this._templateRef);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DigitOnlyDirective } from './digit-only.directive';
|
|
2
|
+
import { MatRowClickableDirective } from './mat-row-clickable.directive';
|
|
3
|
+
import { IfWidthIsDirective } from './if-width-is.directive';
|
|
4
|
+
import { OnemRvaColorDirective } from './color.directive';
|
|
5
|
+
import { OnemrvaMaskDirective } from './mask.directive';
|
|
6
|
+
import {OnemRvaClipboardDirective} from "./clipboard.directive";
|
|
7
|
+
|
|
8
|
+
export const directives: any[] = [
|
|
9
|
+
DigitOnlyDirective,
|
|
10
|
+
MatRowClickableDirective,
|
|
11
|
+
OnemRvaClipboardDirective,
|
|
12
|
+
IfWidthIsDirective,
|
|
13
|
+
OnemRvaColorDirective,
|
|
14
|
+
OnemrvaMaskDirective,
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export * from './digit-only.directive';
|
|
18
|
+
export * from './mat-row-clickable.directive';
|
|
19
|
+
export * from './if-width-is.directive';
|
|
20
|
+
export * from './color.directive';
|
|
21
|
+
export * from './mask.directive';
|
|
22
|
+
export * from './clipboard.directive';
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
const UNDO_STACK_MAX_LENGTH = 50;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 0: digits
|
|
7
|
+
* A: letters (uppercase or lowercase) and digits
|
|
8
|
+
* S: only letters (uppercase or lowercase)
|
|
9
|
+
* U: only letters uppercase
|
|
10
|
+
* L: only letters lowercase
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
@Directive({
|
|
14
|
+
selector: '[onemrvamask]',
|
|
15
|
+
})
|
|
16
|
+
export class OnemrvaMaskDirective {
|
|
17
|
+
@Input() onemrvamask!: string;
|
|
18
|
+
|
|
19
|
+
private inputStack: string[] = ['']; // Stack for undo/redo
|
|
20
|
+
private stackIdx: number = 0; // Current index in undo/redo stack
|
|
21
|
+
|
|
22
|
+
markForDelete = false;
|
|
23
|
+
|
|
24
|
+
@HostListener('keydown', ['$event']) onKeyDown(event: KeyboardEvent) {
|
|
25
|
+
// Allow functional keystrokes
|
|
26
|
+
let e = <KeyboardEvent>event;
|
|
27
|
+
|
|
28
|
+
let specialKeys = [
|
|
29
|
+
'Tab',
|
|
30
|
+
'Enter',
|
|
31
|
+
'ArrowLeft',
|
|
32
|
+
'ArrowRight',
|
|
33
|
+
'ArrowUp',
|
|
34
|
+
'ArrowDown',
|
|
35
|
+
'Home',
|
|
36
|
+
'End',
|
|
37
|
+
];
|
|
38
|
+
if (specialKeys.indexOf(e.key) >= 0) return;
|
|
39
|
+
|
|
40
|
+
specialKeys = ['Backspace', 'Delete'];
|
|
41
|
+
if (specialKeys.indexOf(e.key) >= 0) {
|
|
42
|
+
this.markForDelete = true;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Undo
|
|
47
|
+
if (e.ctrlKey && 'z' === e.key && this.stackIdx > 0) {
|
|
48
|
+
this.stackIdx--;
|
|
49
|
+
this.el.nativeElement.value = this.inputStack[this.stackIdx];
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Redo
|
|
54
|
+
if (
|
|
55
|
+
e.ctrlKey &&
|
|
56
|
+
'u' === e.key &&
|
|
57
|
+
this.stackIdx < this.inputStack.length - 1
|
|
58
|
+
) {
|
|
59
|
+
this.stackIdx++;
|
|
60
|
+
this.el.nativeElement.value = this.inputStack[this.stackIdx];
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const allowedCtrlShortcuts = ['a', 'c', 'x', 'v'];
|
|
65
|
+
if (e.ctrlKey && allowedCtrlShortcuts.indexOf(e.key) >= 0) return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@HostListener('input', ['$event']) onInput(event: any) {
|
|
69
|
+
let selectionStart = event.target?.selectionStart;
|
|
70
|
+
|
|
71
|
+
/*
|
|
72
|
+
* Step 1
|
|
73
|
+
* This block lets a delete execution if the new value is compliant.
|
|
74
|
+
* There are 2 deletion cases, after which the new value is not compliant
|
|
75
|
+
* - dd/MX/yyyy: X is deleted, then the date becomes dd/M/yyyy
|
|
76
|
+
* This case is resolved by step 2 and the date will become: dd/My/yyy
|
|
77
|
+
* - dd/MMXyyyy: the last '/' is deleted, then the date becomes dd/MMyyyy
|
|
78
|
+
* In this particular case, step 2 will automatically add the missing slash, so the date will remain dd/MM/yyyy
|
|
79
|
+
* The issue is that the caret position will remain stuck where it was, so if the user keeps hitting the delete key, nothing will change at all. This is resolved in step 4 by moving the caret to the left
|
|
80
|
+
*/
|
|
81
|
+
if (this.markForDelete) {
|
|
82
|
+
const isCompliantAfterDeletion =
|
|
83
|
+
[...this.el.nativeElement.value].findIndex((char, idx) => {
|
|
84
|
+
let rule = this.onemrvamask[idx];
|
|
85
|
+
|
|
86
|
+
if (rule === null || rule === undefined) return true;
|
|
87
|
+
|
|
88
|
+
if (isNaN(char) && char.toLowerCase() === char.toUpperCase())
|
|
89
|
+
return char !== rule;
|
|
90
|
+
|
|
91
|
+
return !isAllowed(rule, char);
|
|
92
|
+
}) < 0;
|
|
93
|
+
|
|
94
|
+
if (isCompliantAfterDeletion) {
|
|
95
|
+
this.markForDelete = false;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Step 2
|
|
101
|
+
const compliantValue = [...this.el.nativeElement.value]
|
|
102
|
+
.filter((char) => isAllowed('A', char)) // Necessary when several special char in a row
|
|
103
|
+
.reduce((newValue, char) => {
|
|
104
|
+
let idx = newValue.length;
|
|
105
|
+
let rule = this.onemrvamask[idx];
|
|
106
|
+
|
|
107
|
+
if (rule === null || rule === undefined) return newValue;
|
|
108
|
+
|
|
109
|
+
newValue = !isAllowed(rule, char) ? newValue : newValue + char;
|
|
110
|
+
|
|
111
|
+
// Add all trailing special characters
|
|
112
|
+
let nextRule = this.onemrvamask[++idx];
|
|
113
|
+
let i = 0;
|
|
114
|
+
|
|
115
|
+
while (
|
|
116
|
+
nextRule !== null &&
|
|
117
|
+
nextRule !== undefined &&
|
|
118
|
+
['0', 'A', 'S', 'U', 'L'].indexOf(nextRule) < 0
|
|
119
|
+
) {
|
|
120
|
+
i++;
|
|
121
|
+
newValue += nextRule;
|
|
122
|
+
nextRule = this.onemrvamask[++idx];
|
|
123
|
+
|
|
124
|
+
if (i > 50) break;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return newValue;
|
|
128
|
+
}, '');
|
|
129
|
+
|
|
130
|
+
this.el.nativeElement.value = compliantValue;
|
|
131
|
+
|
|
132
|
+
// Step 3 - Find the new cursor position. If the last new character is just before special character, move the caret after
|
|
133
|
+
let idx = selectionStart;
|
|
134
|
+
let nextRule = this.onemrvamask[idx];
|
|
135
|
+
while (
|
|
136
|
+
nextRule !== null &&
|
|
137
|
+
nextRule !== undefined &&
|
|
138
|
+
['0', 'A', 'S', 'U', 'L'].indexOf(nextRule) < 0
|
|
139
|
+
) {
|
|
140
|
+
idx++;
|
|
141
|
+
nextRule = this.onemrvamask[idx];
|
|
142
|
+
if (
|
|
143
|
+
nextRule !== null &&
|
|
144
|
+
nextRule &&
|
|
145
|
+
['0', 'A', 'S', 'U', 'L'].indexOf(nextRule) < 0
|
|
146
|
+
) {
|
|
147
|
+
} else break;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Step 4 - Update inputStack only when all characters are processed
|
|
151
|
+
if (compliantValue !== this.inputStack[this.stackIdx]) {
|
|
152
|
+
this.stackIdx++;
|
|
153
|
+
|
|
154
|
+
let stack =
|
|
155
|
+
this.stackIdx > UNDO_STACK_MAX_LENGTH - 1
|
|
156
|
+
? this.inputStack.slice(1)
|
|
157
|
+
: this.inputStack.splice(0, this.stackIdx);
|
|
158
|
+
this.stackIdx =
|
|
159
|
+
this.stackIdx > UNDO_STACK_MAX_LENGTH - 1
|
|
160
|
+
? UNDO_STACK_MAX_LENGTH - 1
|
|
161
|
+
: this.stackIdx;
|
|
162
|
+
|
|
163
|
+
this.inputStack = [...stack, compliantValue];
|
|
164
|
+
|
|
165
|
+
this.el.nativeElement.setSelectionRange(idx, idx);
|
|
166
|
+
} else {
|
|
167
|
+
if (this.markForDelete) {
|
|
168
|
+
let i = idx - 1;
|
|
169
|
+
/* Finds the last rule character before the special character.
|
|
170
|
+
* e.g: (nnn)/nnnn
|
|
171
|
+
* If the user tries to delete '/', the caret will move just before ')'
|
|
172
|
+
*/
|
|
173
|
+
while (i > 0) {
|
|
174
|
+
const previousRule = this.onemrvamask[i - 1];
|
|
175
|
+
if (['0', 'A', 'S', 'U', 'L'].indexOf(previousRule) < 0) {
|
|
176
|
+
i--;
|
|
177
|
+
} else break;
|
|
178
|
+
}
|
|
179
|
+
this.el.nativeElement.setSelectionRange(i, i);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
this.markForDelete = false;
|
|
183
|
+
}
|
|
184
|
+
constructor(private el: ElementRef) {}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function isAllowed(rule: string, character: string) {
|
|
188
|
+
switch (rule) {
|
|
189
|
+
// Any digit
|
|
190
|
+
case '0':
|
|
191
|
+
if (character === ' ') return false;
|
|
192
|
+
return !isNaN(+character);
|
|
193
|
+
|
|
194
|
+
// A: letters (uppercase or lowercase) and digits
|
|
195
|
+
case 'A':
|
|
196
|
+
if (character === ' ') return false;
|
|
197
|
+
return (
|
|
198
|
+
!isNaN(+character) || character.toLowerCase() != character.toUpperCase()
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// only letters (uppercase or lowercase)
|
|
202
|
+
case 'S':
|
|
203
|
+
return character.toLowerCase() != character.toUpperCase();
|
|
204
|
+
|
|
205
|
+
// only uppercase letters
|
|
206
|
+
case 'U':
|
|
207
|
+
return (
|
|
208
|
+
character.toLowerCase() != character.toUpperCase() &&
|
|
209
|
+
character === character.toUpperCase()
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// only lowercase letters
|
|
213
|
+
case 'L':
|
|
214
|
+
return (
|
|
215
|
+
character.toLowerCase() != character.toUpperCase() &&
|
|
216
|
+
character === character.toLowerCase()
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return false;
|
|
221
|
+
}
|
package/projects/onemrva/design-system/shared/src/lib/directives/mat-row-clickable.directive.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {Directive, ElementRef, EventEmitter, HostBinding, HostListener, Input, Output} from '@angular/core';
|
|
2
|
+
import {OnemrvaMatColor} from "@onemrvapublic/design-system/utils";
|
|
3
|
+
|
|
4
|
+
@Directive({
|
|
5
|
+
selector: '[mat-row-clickable]'
|
|
6
|
+
})
|
|
7
|
+
export class MatRowClickableDirective {
|
|
8
|
+
|
|
9
|
+
@Output('mat-row-clickable')
|
|
10
|
+
matRowClickable: EventEmitter<any> = new EventEmitter<any>();
|
|
11
|
+
|
|
12
|
+
@Input()
|
|
13
|
+
color: OnemrvaMatColor = '';
|
|
14
|
+
|
|
15
|
+
constructor(private el:ElementRef) {}
|
|
16
|
+
|
|
17
|
+
@HostListener('click', ['$event'])
|
|
18
|
+
click(event: any) {
|
|
19
|
+
this.matRowClickable.emit();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@HostBinding('class.onemrva-clickable-row')
|
|
23
|
+
public cssClass = true;
|
|
24
|
+
|
|
25
|
+
ngOnInit() {
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @hidden @internal */
|
|
29
|
+
@HostBinding('class.mat-primary')
|
|
30
|
+
public get _isPrimary(): boolean {
|
|
31
|
+
return this.color === OnemrvaMatColor.PRIMARY;
|
|
32
|
+
}
|
|
33
|
+
/** @hidden @internal */
|
|
34
|
+
@HostBinding('class.mat-accent')
|
|
35
|
+
public get _isAccent(): boolean {
|
|
36
|
+
return this.color === OnemrvaMatColor.ACCENT;
|
|
37
|
+
}
|
|
38
|
+
/** @hidden @internal */
|
|
39
|
+
@HostBinding('class.mat-error')
|
|
40
|
+
public get _isError(): boolean {
|
|
41
|
+
return this.color === OnemrvaMatColor.ERROR;
|
|
42
|
+
}
|
|
43
|
+
/** @hidden @internal */
|
|
44
|
+
@HostBinding('class.mat-warn')
|
|
45
|
+
public get _isWarn(): boolean {
|
|
46
|
+
return this.color === OnemrvaMatColor.WARN;
|
|
47
|
+
}
|
|
48
|
+
/** @hidden @internal */
|
|
49
|
+
@HostBinding('class.mat-success')
|
|
50
|
+
public get _isSuccess(): boolean {
|
|
51
|
+
return this.color === OnemrvaMatColor.SUCCESS;
|
|
52
|
+
}
|
|
53
|
+
/** @hidden @internal */
|
|
54
|
+
@HostBinding('class.mat-info')
|
|
55
|
+
public get _isInfo(): boolean {
|
|
56
|
+
return this.color === OnemrvaMatColor.INFO;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Pipe, PipeTransform } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Pipe({
|
|
4
|
+
name: 'onemrvaBce',
|
|
5
|
+
standalone: true,
|
|
6
|
+
})
|
|
7
|
+
export class OnemrvaBcePipe implements PipeTransform {
|
|
8
|
+
transform(value: string): string {
|
|
9
|
+
let strOut = value.trim().replace(/\/|\.|\-/g, '');
|
|
10
|
+
|
|
11
|
+
if (strOut.length !== 10) return '?01?.???.???';
|
|
12
|
+
|
|
13
|
+
return `${strOut.substring(0, 4)}.${strOut.substring(
|
|
14
|
+
4,
|
|
15
|
+
7
|
|
16
|
+
)}.${strOut.substring(7, 10)}`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Pipe, PipeTransform } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Pipe({
|
|
4
|
+
name: 'onemrvaNiss',
|
|
5
|
+
standalone: true,
|
|
6
|
+
})
|
|
7
|
+
export class OnemrvaNissPipe implements PipeTransform {
|
|
8
|
+
transform(value: string): string {
|
|
9
|
+
let strOut = value.trim().replace(/\/|\.|\-/g, '');
|
|
10
|
+
|
|
11
|
+
if (strOut.length !== 11) return '??01??/???-??';
|
|
12
|
+
|
|
13
|
+
return `${strOut.substring(0, 6)}/${strOut.substring(
|
|
14
|
+
6,
|
|
15
|
+
9
|
|
16
|
+
)}-${strOut.substring(9, 11)}`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { NgModule } from '@angular/core';
|
|
3
|
+
import { directives } from './directives';
|
|
4
|
+
|
|
5
|
+
@NgModule({
|
|
6
|
+
declarations: [...directives],
|
|
7
|
+
imports: [CommonModule],
|
|
8
|
+
exports: [...directives, CommonModule],
|
|
9
|
+
})
|
|
10
|
+
export class OnemrvaSharedModule {}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ValidationErrors } from '@angular/forms';
|
|
2
|
+
import { AbstractControl, ValidatorFn } from '@angular/forms';
|
|
3
|
+
|
|
4
|
+
export class OnemrvaValidators {
|
|
5
|
+
static bce(required = false): ValidatorFn {
|
|
6
|
+
return (control: AbstractControl): ValidationErrors | null => {
|
|
7
|
+
if (control.value === null || control.value.trim() === '') {
|
|
8
|
+
return required ? null : { bceNull: true };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let bceCandidate = control.value.trim().replace(/\/|\.|\-/g, '');
|
|
12
|
+
|
|
13
|
+
if (bceCandidate.length !== 10)
|
|
14
|
+
return { bceLengthError: { value: bceCandidate } };
|
|
15
|
+
|
|
16
|
+
if (Number.isNaN(+bceCandidate))
|
|
17
|
+
return { bceNan: { value: bceCandidate } };
|
|
18
|
+
|
|
19
|
+
return null;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static niss(required = false): ValidatorFn {
|
|
24
|
+
return (control: AbstractControl): ValidationErrors | null => {
|
|
25
|
+
if (control.value === null || control.value.trim() === '')
|
|
26
|
+
return required ? null : { nissNull: true };
|
|
27
|
+
|
|
28
|
+
let nissCandidate = control.value.trim().replace(/\/|\.|\-/g, '');
|
|
29
|
+
|
|
30
|
+
if (nissCandidate.length !== 11)
|
|
31
|
+
return { nissLengthError: { value: nissCandidate } };
|
|
32
|
+
|
|
33
|
+
if (Number.isNaN(+nissCandidate))
|
|
34
|
+
return { bceNan: { value: nissCandidate } };
|
|
35
|
+
|
|
36
|
+
const date = +nissCandidate.substring(4, 6);
|
|
37
|
+
const month = +nissCandidate.substring(2, 4) - 1;
|
|
38
|
+
const currentYear = new Date().getFullYear() - 2000;
|
|
39
|
+
let year = +nissCandidate.substring(0, 2);
|
|
40
|
+
year = year > currentYear ? 1900 + year : 2000 + year;
|
|
41
|
+
|
|
42
|
+
// Check month
|
|
43
|
+
if (month > 11) return { nissInvalidMonth: { value: nissCandidate } };
|
|
44
|
+
|
|
45
|
+
// Check date
|
|
46
|
+
if (date > 31) return { nissInvalidDate: { value: nissCandidate } };
|
|
47
|
+
|
|
48
|
+
// Check date consistency
|
|
49
|
+
const d = new Date(year, month, date);
|
|
50
|
+
if (d.getDate() !== date || d.getMonth() !== month)
|
|
51
|
+
// This catches errors such as 32 Jan, which becomes 1 Feb inside the date object
|
|
52
|
+
return { nissInconsistentDate: { value: nissCandidate } };
|
|
53
|
+
|
|
54
|
+
// Check digit
|
|
55
|
+
let nissStart =
|
|
56
|
+
year < 2000
|
|
57
|
+
? +nissCandidate.substring(0, 9)
|
|
58
|
+
: +('2' + nissCandidate.substring(0, 9));
|
|
59
|
+
|
|
60
|
+
if (97 - (nissStart % 97) !== +nissCandidate.substring(9))
|
|
61
|
+
return { nissCheckDigitError: { value: nissCandidate } };
|
|
62
|
+
|
|
63
|
+
return null;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|