newportsite 1.1.3

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.
Files changed (131) hide show
  1. package/newportsite-1.1.3.tgz +0 -0
  2. package/ng-package.json +7 -0
  3. package/obfuscate.js +70 -0
  4. package/package.json +15 -0
  5. package/src/lib/app.component.ts +47 -0
  6. package/src/lib/app.routing.ts +38 -0
  7. package/src/lib/auth/alert.component.html +5 -0
  8. package/src/lib/auth/alert.component.ts +24 -0
  9. package/src/lib/auth/auth.component.html +1 -0
  10. package/src/lib/auth/auth.component.ts +10 -0
  11. package/src/lib/auth/auth.routes.ts +16 -0
  12. package/src/lib/auth/index.ts +4 -0
  13. package/src/lib/auth/login.component.html +87 -0
  14. package/src/lib/auth/login.component.ts +158 -0
  15. package/src/lib/auth/models/index.ts +1 -0
  16. package/src/lib/auth/models/user.ts +25 -0
  17. package/src/lib/auth/register.component.html +157 -0
  18. package/src/lib/auth/register.component.ts +219 -0
  19. package/src/lib/auth/services/alert.service.ts +47 -0
  20. package/src/lib/auth/services/auth.service.ts +28 -0
  21. package/src/lib/auth/services/index.ts +3 -0
  22. package/src/lib/auth/services/user.service.spec.ts +112 -0
  23. package/src/lib/auth/services/user.service.ts +47 -0
  24. package/src/lib/common/card.component.html +72 -0
  25. package/src/lib/common/card.component.ts +102 -0
  26. package/src/lib/common/commands.component.html +8 -0
  27. package/src/lib/common/commands.component.ts +42 -0
  28. package/src/lib/common/context.component.html +9 -0
  29. package/src/lib/common/context.component.ts +38 -0
  30. package/src/lib/common/grid.component.html +20 -0
  31. package/src/lib/common/grid.component.ts +747 -0
  32. package/src/lib/common/index.ts +9 -0
  33. package/src/lib/common/loader.component.html +5 -0
  34. package/src/lib/common/loader.component.ts +27 -0
  35. package/src/lib/common/lookup.component.html +29 -0
  36. package/src/lib/common/lookup.component.ts +115 -0
  37. package/src/lib/common/messagebox.component.html +39 -0
  38. package/src/lib/common/messagebox.component.ts +74 -0
  39. package/src/lib/common/theme-toggle.component.ts +139 -0
  40. package/src/lib/config.ts +62 -0
  41. package/src/lib/containers/default-layout/default-layout.component.html +191 -0
  42. package/src/lib/containers/default-layout/default-layout.component.ts +158 -0
  43. package/src/lib/containers/default-layout/index.ts +1 -0
  44. package/src/lib/containers/index.ts +1 -0
  45. package/src/lib/directives/component.draggable.ts +80 -0
  46. package/src/lib/directives/index.ts +2 -0
  47. package/src/lib/directives/input.directive.spec.ts +158 -0
  48. package/src/lib/directives/input.directive.ts +210 -0
  49. package/src/lib/home/dashboard/dashboard.component.html +38 -0
  50. package/src/lib/home/dashboard/dashboard.component.ts +50 -0
  51. package/src/lib/home/dashboard/index.ts +1 -0
  52. package/src/lib/home/index.component.html +1 -0
  53. package/src/lib/home/index.component.ts +10 -0
  54. package/src/lib/home/index.routes.ts +29 -0
  55. package/src/lib/home/index.ts +1 -0
  56. package/src/lib/home/info/index.ts +1 -0
  57. package/src/lib/home/info/info.component.css +476 -0
  58. package/src/lib/home/info/info.component.html +174 -0
  59. package/src/lib/home/info/info.component.ts +287 -0
  60. package/src/lib/home/model/article.component.html +10 -0
  61. package/src/lib/home/model/article.component.ts +50 -0
  62. package/src/lib/home/model/barchart.component.html +8 -0
  63. package/src/lib/home/model/barchart.component.ts +59 -0
  64. package/src/lib/home/model/index.ts +7 -0
  65. package/src/lib/home/model/itemdetail.component.html +25 -0
  66. package/src/lib/home/model/itemdetail.component.ts +93 -0
  67. package/src/lib/home/model/itemtab.component.html +25 -0
  68. package/src/lib/home/model/itemtab.component.ts +105 -0
  69. package/src/lib/home/model/model.component.html +121 -0
  70. package/src/lib/home/model/model.component.ts +510 -0
  71. package/src/lib/home/model/modeltoolbar.component.html +111 -0
  72. package/src/lib/home/model/modeltoolbar.component.ts +157 -0
  73. package/src/lib/home/model/navigation.component.html +86 -0
  74. package/src/lib/home/model/navigation.component.ts +247 -0
  75. package/src/lib/home/model/services/index.ts +1 -0
  76. package/src/lib/home/model/services/model.service.spec.ts +423 -0
  77. package/src/lib/home/model/services/model.service.ts +319 -0
  78. package/src/lib/home/modelsearch/index.ts +1 -0
  79. package/src/lib/home/modelsearch/modelsearch.component.html +124 -0
  80. package/src/lib/home/modelsearch/modelsearch.component.ts +453 -0
  81. package/src/lib/interfaces/data.interface.ts +131 -0
  82. package/src/lib/interfaces/index.ts +2 -0
  83. package/src/lib/interfaces/item.interface.ts +438 -0
  84. package/src/lib/players/lookup/lookup.directive.ts +6 -0
  85. package/src/lib/players/lookup/lookup.item.component.ts +37 -0
  86. package/src/lib/players/lookup/lookup.item.ts +9 -0
  87. package/src/lib/players/lookup/lookup.player.component.ts +59 -0
  88. package/src/lib/players/lookup/lookup.selector.component.ts +41 -0
  89. package/src/lib/players/model/model.directive.ts +6 -0
  90. package/src/lib/players/model/model.item.component.spec.ts +311 -0
  91. package/src/lib/players/model/model.item.component.ts +3457 -0
  92. package/src/lib/players/model/model.item.ts +9 -0
  93. package/src/lib/players/model/model.player.component.ts +109 -0
  94. package/src/lib/players/model/model.selector.component.ts +59 -0
  95. package/src/lib/scheduler/scheduler.component.html +13 -0
  96. package/src/lib/scheduler/scheduler.component.scss +6 -0
  97. package/src/lib/scheduler/scheduler.component.ts +296 -0
  98. package/src/lib/scheduler/scheduler.routes.ts +15 -0
  99. package/src/lib/scheduler/schedulerdialog.component.html +72 -0
  100. package/src/lib/scheduler/schedulerdialog.component.ts +208 -0
  101. package/src/lib/scheduler/services/scheduler.service.ts +133 -0
  102. package/src/lib/services/auth-state.service.ts +129 -0
  103. package/src/lib/services/auth.interceptor.spec.ts +144 -0
  104. package/src/lib/services/auth.interceptor.ts +44 -0
  105. package/src/lib/services/cache.service.spec.ts +143 -0
  106. package/src/lib/services/cache.service.ts +71 -0
  107. package/src/lib/services/global-error-handler.spec.ts +39 -0
  108. package/src/lib/services/global-error-handler.ts +28 -0
  109. package/src/lib/services/global.service.spec.ts +801 -0
  110. package/src/lib/services/global.service.ts +724 -0
  111. package/src/lib/services/message.service.ts +556 -0
  112. package/src/lib/services/theme.service.ts +96 -0
  113. package/src/lib/template/authtemplate.component.html +6 -0
  114. package/src/lib/template/authtemplate.component.ts +13 -0
  115. package/src/lib/template/basetemplate.component.html +7 -0
  116. package/src/lib/template/basetemplate.component.ts +13 -0
  117. package/src/lib/template/index.ts +3 -0
  118. package/src/lib/template/modeltemplate.component.html +7 -0
  119. package/src/lib/template/modeltemplate.component.ts +21 -0
  120. package/src/lib/utils/piva.spec.ts +56 -0
  121. package/src/lib/utils/piva.ts +29 -0
  122. package/src/lib/validators/email.validator.spec.ts +57 -0
  123. package/src/lib/validators/email.validator.ts +17 -0
  124. package/src/lib/validators/equalPasswords.validator.spec.ts +54 -0
  125. package/src/lib/validators/equalPasswords.validator.ts +17 -0
  126. package/src/lib/validators/index.ts +2 -0
  127. package/src/lib/version.ts +1 -0
  128. package/src/public-api.ts +64 -0
  129. package/src/typings.d.ts +2 -0
  130. package/tsconfig.lib.json +18 -0
  131. package/tsconfig.lib.prod.json +9 -0
@@ -0,0 +1,191 @@
1
+ <div class="container-fluid" [class.pe-none]="gsv?.getLoading()">
2
+ <div class="row flex-nowrap">
3
+ <div class="col-auto px-0">
4
+ <div id="sidebar" class="collapse collapse-horizontal show">
5
+ <nav
6
+ id="sidebar-nav"
7
+ class="min-vh-100"
8
+ aria-label="Navigazione principale"
9
+ [class.pe-none]="gsv?.getModalActive()">
10
+ <div class="sidebar-logo">
11
+ <a title="Newport - {{ version }}" aria-label="Newport - logout" role="button" (click)="logOff()">
12
+ <i class="bi bi-grid-1x2-fill" aria-hidden="true"></i>
13
+ </a>
14
+ </div>
15
+ <ul class="sidebar-items" role="menubar">
16
+ @if (gsv?.isLogged()) {
17
+ <li class="sidebar-item" role="none">
18
+ <a
19
+ role="menuitem"
20
+ [attr.aria-current]="isDashBoard() ? 'page' : null"
21
+ [attr.aria-label]="msg?.get('app.dashboard')"
22
+ (click)="viewChange(msg?.get('app.dashboard'))">
23
+ <i
24
+ [ngClass]="{ 'has-selected': isDashBoard() }"
25
+ class="bi bi-building-gear"
26
+ aria-hidden="true"
27
+ title="{{ msg?.get('app.dashboard') }}"></i>
28
+ </a>
29
+ </li>
30
+ }
31
+ @if (gsv?.isLogged()) {
32
+ <li class="sidebar-item" role="none">
33
+ <a
34
+ role="menuitem"
35
+ [attr.aria-current]="isCalendar() ? 'page' : null"
36
+ [attr.aria-label]="msg?.get('app.calendar')"
37
+ (click)="viewChange(msg?.get('app.calendar'))">
38
+ <i
39
+ [ngClass]="{ 'has-selected': isCalendar() }"
40
+ class="bi bi-calendar"
41
+ aria-hidden="true"
42
+ title="{{ msg?.get('app.calendar') }}"></i>
43
+ </a>
44
+ </li>
45
+ }
46
+ <li class="sidebar-item" role="none">
47
+ <a
48
+ role="menuitem"
49
+ [attr.aria-current]="isInfo() ? 'page' : null"
50
+ [attr.aria-label]="msg?.get('app.info')"
51
+ (click)="viewChange(msg?.get('app.info'))">
52
+ <i
53
+ [ngClass]="{ 'has-selected': isInfo() }"
54
+ class="bi bi-info-lg"
55
+ aria-hidden="true"
56
+ title="{{ msg?.get('app.info') }}"></i>
57
+ </a>
58
+ </li>
59
+ </ul>
60
+ </nav>
61
+ </div>
62
+ </div>
63
+ <div class="col p-0 fillvp">
64
+ <header class="app-toolbar" [class.pe-none]="gsv?.getModalActive()">
65
+ <button
66
+ type="button"
67
+ class="toolbar-btn"
68
+ data-bs-toggle="collapse"
69
+ data-bs-target="#sidebar"
70
+ [attr.aria-expanded]="!sidebarState"
71
+ aria-controls="sidebar"
72
+ aria-label="Mostra/nascondi menu"
73
+ (click)="sidebarToggle()"
74
+ title="Menu">
75
+ <i
76
+ aria-hidden="true"
77
+ [ngClass]="{
78
+ bi: sidebarState,
79
+ 'bi-chevron-compact-left': !sidebarState,
80
+ 'bi-chevron-compact-right': sidebarState,
81
+ }"></i>
82
+ </button>
83
+ <div class="btn-group" dropdown>
84
+ <button
85
+ dropdownToggle
86
+ type="button"
87
+ class="btn dropdown-toggle"
88
+ id="button-language"
89
+ aria-label="Seleziona lingua"
90
+ aria-controls="dropdown-language">
91
+ {{ gsv?.getLng() }}
92
+ </button>
93
+ <ul
94
+ id="dropdown-language"
95
+ *dropdownMenu
96
+ class="dropdown-menu"
97
+ role="menu"
98
+ aria-labelledby="button-language">
99
+ <li role="none">
100
+ <a
101
+ class="dropdown-item text-center"
102
+ role="menuitem"
103
+ (click)="languageChange('IT')"
104
+ >IT</a
105
+ >
106
+ </li>
107
+ <li role="none">
108
+ <a
109
+ class="dropdown-item text-center"
110
+ role="menuitem"
111
+ (click)="languageChange('EN')"
112
+ >EN</a
113
+ >
114
+ </li>
115
+ </ul>
116
+ </div>
117
+ <app-theme-toggle></app-theme-toggle>
118
+ @if (gsv?.isLogged()) {
119
+ <span class="btn-group" dropdown>
120
+ <button
121
+ id="button-basic-year"
122
+ dropdownToggle
123
+ type="button"
124
+ class="btn dropdown-toggle"
125
+ aria-label="Seleziona anno"
126
+ aria-controls="dropdown-basic-year">
127
+ {{ gsv?.getYear() }}
128
+ </button>
129
+ <ul
130
+ id="dropdown-basic-year"
131
+ *dropdownMenu
132
+ class="dropdown-menu"
133
+ role="menu"
134
+ aria-labelledby="button-basic-year">
135
+ <li role="none">
136
+ @for (year of gsv?.getYears(); track year) {
137
+ <a
138
+ class="dropdown-item text-center"
139
+ role="menuitem"
140
+ (click)="yearChange(year)"
141
+ >{{ year }}</a
142
+ >
143
+ }
144
+ </li>
145
+ </ul>
146
+ </span>
147
+ }
148
+ <div class="ms-auto">
149
+ @if (gsv?.isLogged()) {
150
+ <span
151
+ class="btn-group d-flex"
152
+ dropdown
153
+ title="{{ gsv?.loggedUser() }}">
154
+ <a
155
+ id="button-basic"
156
+ dropdownToggle
157
+ type="button"
158
+ role="button"
159
+ class="btn dropdown-toggle"
160
+ [attr.aria-label]="'Utente: ' + gsv?.loggedUser()"
161
+ aria-controls="dropdown-basic">
162
+ <i class="bi bi-person" aria-hidden="true" title="{{ gsv?.loggedUser() }}"></i>
163
+ </a>
164
+ <ul
165
+ id="dropdown-basic"
166
+ *dropdownMenu
167
+ class="dropdown-menu dropdown-menu-end"
168
+ role="menu"
169
+ aria-labelledby="button-basic">
170
+ <li role="none">
171
+ <a
172
+ class="dropdown-item"
173
+ role="menuitem"
174
+ href="#"
175
+ (click)="logOff()"
176
+ title="{{ gsv?.loggedUser() }}">
177
+ <span class="bi bi-person-lock" aria-hidden="true"></span>
178
+ {{ msg?.get('app.logoff') }}
179
+ </a>
180
+ </li>
181
+ </ul>
182
+ </span>
183
+ }
184
+ </div>
185
+ </header>
186
+ <main class="main">
187
+ <router-outlet></router-outlet>
188
+ </main>
189
+ </div>
190
+ </div>
191
+ </div>
@@ -0,0 +1,158 @@
1
+ import {
2
+ Component,
3
+ AfterViewInit,
4
+ ChangeDetectionStrategy,
5
+ ChangeDetectorRef,
6
+ DestroyRef,
7
+ PLATFORM_ID,
8
+ inject,
9
+ CUSTOM_ELEMENTS_SCHEMA,
10
+ } from '@angular/core';
11
+ import { isPlatformBrowser } from '@angular/common';
12
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
13
+
14
+ import { GlobalService } from '../../services/global.service';
15
+
16
+ import { AppMessageService } from '../../services/message.service';
17
+
18
+ import { PACKAGE_VERSION } from '../../version';
19
+ import { NgClass } from '@angular/common';
20
+ import {
21
+ BsDropdownDirective,
22
+ BsDropdownToggleDirective,
23
+ BsDropdownMenuDirective,
24
+ } from 'ngx-bootstrap/dropdown';
25
+ import { RouterOutlet } from '@angular/router';
26
+ import { ThemeToggleComponent } from '../../common/theme-toggle.component';
27
+
28
+ @Component({
29
+ providers: [AppMessageService],
30
+ selector: 'app-dashboard',
31
+ templateUrl: './default-layout.component.html',
32
+ changeDetection: ChangeDetectionStrategy.OnPush,
33
+ imports: [
34
+ NgClass,
35
+ BsDropdownDirective,
36
+ BsDropdownToggleDirective,
37
+ BsDropdownMenuDirective,
38
+ RouterOutlet,
39
+ ThemeToggleComponent,
40
+ ],
41
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
42
+ })
43
+ export class DefaultLayoutComponent implements AfterViewInit {
44
+ gsv = inject(GlobalService);
45
+ msg = inject(AppMessageService);
46
+ private cdr = inject(ChangeDetectorRef);
47
+ private destroyRef = inject(DestroyRef);
48
+ private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
49
+
50
+ public active!: string;
51
+
52
+ public languagevalue = '';
53
+ public viewvalue = 'Dashboard';
54
+ public element: HTMLElement = this.isBrowser ? document.body : ({} as HTMLElement);
55
+
56
+ public sidebarState: boolean = false;
57
+
58
+ public version: string;
59
+
60
+ constructor() {
61
+ const gsv = this.gsv;
62
+
63
+ this.languagevalue = this.gsv.getLng();
64
+ this.active = gsv.getActiveLink();
65
+ this.version = PACKAGE_VERSION;
66
+
67
+ this.gsv
68
+ .getStateChanged()
69
+ .pipe(takeUntilDestroyed(this.destroyRef))
70
+ .subscribe(() => this.cdr.markForCheck());
71
+
72
+ this.gsv
73
+ .getModalActiveChanges()
74
+ .pipe(takeUntilDestroyed(this.destroyRef))
75
+ .subscribe(() => this.cdr.markForCheck());
76
+ }
77
+
78
+ /** Lifecycle: syncs the active link after the view initialises. */
79
+ public ngAfterViewInit() {
80
+ this.active = this.gsv.getActiveLink();
81
+ }
82
+
83
+ /** Opens the given URL in an external browser tab. */
84
+ public externalnavigate(url: string) {
85
+ this.gsv.externalnavigate(url);
86
+ }
87
+
88
+ /** Updates the active language and redirects to Dashboard when already logged in. */
89
+ public languageChange(language: any) {
90
+ this.languagevalue = language;
91
+ this.gsv.setLng(language);
92
+ // only navigate to Dashboard when already logged in
93
+ if (this.gsv.isLogged()) {
94
+ this.viewChange('Dashboard');
95
+ }
96
+ this.cdr.detectChanges();
97
+ }
98
+
99
+ /** Sets the current view label and notifies GlobalService. */
100
+ public viewChange(view: any) {
101
+ this.viewvalue = view;
102
+ this.gsv.setView(view);
103
+ this.cdr.detectChanges();
104
+ }
105
+
106
+ /** Returns true when the active view is the Dashboard. */
107
+ public isDashBoard(): boolean {
108
+ return this.viewvalue === this.msg?.get('app.dashboard');
109
+ }
110
+
111
+ /** Returns true when the active view is the Calendar. */
112
+ public isCalendar(): boolean {
113
+ return this.viewvalue === this.msg?.get('app.calendar');
114
+ }
115
+
116
+ /** Returns true when the active view is the Info page. */
117
+ public isInfo(): boolean {
118
+ return this.viewvalue === this.msg?.get('app.info');
119
+ }
120
+
121
+ /** Updates the active fiscal year and resets the view to Dashboard. */
122
+ public yearChange(year: number) {
123
+ this.gsv.setYear(year);
124
+ this.viewChange('Dashboard');
125
+ this.cdr.detectChanges();
126
+ }
127
+
128
+ /** Returns the display name of the currently authenticated user. */
129
+ public loggedUser(): string {
130
+ return this.gsv.loggedUser();
131
+ }
132
+
133
+ /** Clears localStorage, resets the view, and calls GlobalService.logOff(). */
134
+ public logOff(): void {
135
+ if (this.isBrowser) {
136
+ try {
137
+ localStorage.clear();
138
+ } catch (error) {
139
+ console.error('Error clearing localStorage:', error);
140
+ }
141
+ }
142
+ this.viewChange('Dashboard');
143
+ this.gsv.logOff();
144
+ this.ngAfterViewInit();
145
+ }
146
+
147
+ /** Updates the active sidebar link and navigates to the given route URL. */
148
+ public navigate(url: string) {
149
+ this.active = url;
150
+ this.gsv.setActiveLink(url);
151
+ this.gsv.navigate(url);
152
+ }
153
+
154
+ /** Toggles the sidebar open/closed state. */
155
+ public sidebarToggle() {
156
+ this.sidebarState = !this.sidebarState;
157
+ }
158
+ }
@@ -0,0 +1 @@
1
+ export * from './default-layout.component';
@@ -0,0 +1 @@
1
+ export * from './default-layout';
@@ -0,0 +1,80 @@
1
+ import {
2
+ Directive,
3
+ ElementRef,
4
+ HostListener,
5
+ AfterViewInit,
6
+ OnDestroy,
7
+ inject,
8
+ } from '@angular/core';
9
+ import { GlobalService } from '../services/global.service';
10
+
11
+ /*
12
+ * Directive to add 'drag' support to Ngx Bootstrap modals (https://github.com/valor-software/ngx-bootstrap).
13
+ * Based on this library to enable drag support for an ng-bootstrap modal: https://github.com/mattxu-zz/ngb-modal-draggable
14
+ *
15
+ * Enable by adding the directive to the modal-header element, e.g.:
16
+ *
17
+ * <div class="modal-header" ngxModalDraggable> </div>
18
+ */
19
+
20
+ @Directive({ selector: '[ngxModalDraggable]', standalone: true })
21
+ export class NgxModalDraggableDirective implements AfterViewInit {
22
+ element = inject(ElementRef);
23
+ private gsv = inject(GlobalService, { optional: true });
24
+
25
+ private modalElement!: HTMLElement;
26
+ private topStart!: number;
27
+ private leftStart!: number;
28
+ private isDraggable!: boolean;
29
+ private handleElement!: HTMLElement;
30
+
31
+ /** Lifecycle: sets the host element as the drag handle and fixes the modal position. */
32
+ ngAfterViewInit() {
33
+ let element = this.element.nativeElement;
34
+
35
+ //only make the modal header draggable
36
+ this.handleElement = this.element.nativeElement;
37
+
38
+ //change cursor on the header
39
+ this.handleElement.style.cursor = 'pointer';
40
+
41
+ this.modalElement = element;
42
+ this.modalElement.style.position = 'fixed';
43
+ }
44
+
45
+ /** Records the pointer offset from the modal origin to begin a drag operation. */
46
+ @HostListener('mousedown', ['$event'])
47
+ onMouseDown(event: MouseEvent) {
48
+ this.isDraggable = true;
49
+
50
+ const _s = this.gsv?.getScale() ?? 1;
51
+ this.topStart =
52
+ event.clientY / _s -
53
+ Number(this.modalElement.style.top.replace('px', ''));
54
+ this.leftStart =
55
+ event.clientX / _s -
56
+ Number(this.modalElement.style.left.replace('px', ''));
57
+ }
58
+
59
+ /** Ends the drag operation on mouse button release. */
60
+ @HostListener('document:mouseup', ['$event'])
61
+ onMouseUp(event: MouseEvent) {
62
+ this.isDraggable = false;
63
+ }
64
+
65
+ /** Repositions the modal to follow the pointer while dragging. */
66
+ @HostListener('document:mousemove', ['$event'])
67
+ onMouseMove(event: MouseEvent) {
68
+ if (this.isDraggable) {
69
+ const _s = this.gsv?.getScale() ?? 1;
70
+ this.modalElement.style.top = event.clientY / _s - this.topStart + 'px';
71
+ this.modalElement.style.left = event.clientX / _s - this.leftStart + 'px';
72
+ }
73
+ }
74
+
75
+ /** Ends the drag operation when the pointer leaves the document. */
76
+ @HostListener('document:mouseleave', ['$event'])
77
+ onMouseLeave(event: MouseEvent) {
78
+ this.isDraggable = false;
79
+ }
80
+ }
@@ -0,0 +1,2 @@
1
+ export * from './input.directive';
2
+ export * from './component.draggable';
@@ -0,0 +1,158 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { ElementRef } from '@angular/core';
3
+ import { InputDirective } from './input.directive';
4
+ import { GlobalService } from '../services/global.service';
5
+ import { AppMessageService } from '../services/message.service';
6
+
7
+ // ── Minimal stubs ─────────────────────────────────────────────────────────────
8
+
9
+ const mockGsv = {
10
+ getDialog: () => null,
11
+ getDecimalChar: () => '.',
12
+ setCurrentField: jest.fn(),
13
+ setFieldChanged: jest.fn(),
14
+ };
15
+ const mockMsg = {};
16
+ const mockElementRef = { nativeElement: document.createElement('input') };
17
+
18
+ function key(init: KeyboardEventInit): KeyboardEvent {
19
+ return new KeyboardEvent('keydown', init);
20
+ }
21
+
22
+ // ── Suite ─────────────────────────────────────────────────────────────────────
23
+
24
+ describe('InputDirective', () => {
25
+ let directive: InputDirective;
26
+
27
+ beforeEach(() => {
28
+ TestBed.configureTestingModule({
29
+ providers: [
30
+ InputDirective,
31
+ { provide: ElementRef, useValue: mockElementRef },
32
+ { provide: GlobalService, useValue: mockGsv },
33
+ { provide: AppMessageService, useValue: mockMsg },
34
+ ],
35
+ });
36
+ directive = TestBed.inject(InputDirective);
37
+ });
38
+
39
+ // ── isControlKey() ────────────────────────────────────────────────────────
40
+
41
+ describe('isControlKey()', () => {
42
+ it('returns true for Backspace', () => {
43
+ expect(directive.isControlKey(key({ key: 'Backspace' }))).toBe(true);
44
+ });
45
+
46
+ it('returns true for Tab', () => {
47
+ expect(directive.isControlKey(key({ key: 'Tab' }))).toBe(true);
48
+ });
49
+
50
+ it('returns true for Escape', () => {
51
+ expect(directive.isControlKey(key({ key: 'Escape' }))).toBe(true);
52
+ });
53
+
54
+ it('returns true for Enter', () => {
55
+ expect(directive.isControlKey(key({ key: 'Enter' }))).toBe(true);
56
+ });
57
+
58
+ it('returns true for ArrowLeft', () => {
59
+ expect(directive.isControlKey(key({ key: 'ArrowLeft' }))).toBe(true);
60
+ });
61
+
62
+ it('returns true for ArrowRight', () => {
63
+ expect(directive.isControlKey(key({ key: 'ArrowRight' }))).toBe(true);
64
+ });
65
+
66
+ it('returns true for ArrowUp', () => {
67
+ expect(directive.isControlKey(key({ key: 'ArrowUp' }))).toBe(true);
68
+ });
69
+
70
+ it('returns true for ArrowDown', () => {
71
+ expect(directive.isControlKey(key({ key: 'ArrowDown' }))).toBe(true);
72
+ });
73
+
74
+ it('returns true for F1', () => {
75
+ expect(directive.isControlKey(key({ key: 'F1' }))).toBe(true);
76
+ });
77
+
78
+ it('returns true for F9', () => {
79
+ expect(directive.isControlKey(key({ key: 'F9' }))).toBe(true);
80
+ });
81
+
82
+ it('returns true for F12', () => {
83
+ expect(directive.isControlKey(key({ key: 'F12' }))).toBe(true);
84
+ });
85
+
86
+ it('returns false for F13 (outside the F1–F12 range)', () => {
87
+ expect(directive.isControlKey(key({ key: 'F13' }))).toBe(false);
88
+ });
89
+
90
+ it('returns true when ctrlKey modifier is held', () => {
91
+ expect(directive.isControlKey(key({ key: 'a', ctrlKey: true }))).toBe(true);
92
+ });
93
+
94
+ it('returns true when altKey modifier is held', () => {
95
+ expect(directive.isControlKey(key({ key: 'a', altKey: true }))).toBe(true);
96
+ });
97
+
98
+ it('returns true when shiftKey modifier is held', () => {
99
+ expect(directive.isControlKey(key({ key: 'a', shiftKey: true }))).toBe(true);
100
+ });
101
+
102
+ it('returns false for a plain letter key', () => {
103
+ expect(directive.isControlKey(key({ key: 'a' }))).toBe(false);
104
+ });
105
+
106
+ it('returns false for a plain digit key', () => {
107
+ expect(directive.isControlKey(key({ key: '5' }))).toBe(false);
108
+ });
109
+
110
+ it('returns false for a space character', () => {
111
+ expect(directive.isControlKey(key({ key: ' ' }))).toBe(false);
112
+ });
113
+ });
114
+
115
+ // ── regExpressionMatch() ──────────────────────────────────────────────────
116
+
117
+ describe('regExpressionMatch()', () => {
118
+ it('matches an uppercase letter against [A-Z]', () => {
119
+ expect(directive.regExpressionMatch('[A-Z]', 'A')).toBe(true);
120
+ });
121
+
122
+ it('returns false for a digit against [A-Z]', () => {
123
+ expect(directive.regExpressionMatch('[A-Z]', '1')).toBe(false);
124
+ });
125
+
126
+ it('matches a digit against [0-9]', () => {
127
+ expect(directive.regExpressionMatch('[0-9]', '7')).toBe(true);
128
+ });
129
+
130
+ it('returns false for a letter against [0-9]', () => {
131
+ expect(directive.regExpressionMatch('[0-9]', 'A')).toBe(false);
132
+ });
133
+
134
+ it('uppercases the pattern before matching — [a-z] becomes [A-Z]', () => {
135
+ // The implementation calls rem.toUpperCase() on the pattern.
136
+ // '[a-z]'.toUpperCase() === '[A-Z]', so uppercase keys still match.
137
+ expect(directive.regExpressionMatch('[a-z]', 'A')).toBe(true);
138
+ });
139
+
140
+ it('returns false for an empty key string', () => {
141
+ expect(directive.regExpressionMatch('[A-Z]', '')).toBe(false);
142
+ });
143
+
144
+ it('matches a specific character literal', () => {
145
+ expect(directive.regExpressionMatch('A', 'A')).toBe(true);
146
+ });
147
+
148
+ it('returns false for a character that does not match the literal', () => {
149
+ expect(directive.regExpressionMatch('A', 'B')).toBe(false);
150
+ });
151
+
152
+ it('matches against a union pattern', () => {
153
+ expect(directive.regExpressionMatch('[A-Z0-9]', '5')).toBe(true);
154
+ expect(directive.regExpressionMatch('[A-Z0-9]', 'Z')).toBe(true);
155
+ expect(directive.regExpressionMatch('[A-Z0-9]', '!')).toBe(false);
156
+ });
157
+ });
158
+ });