@nettyapps/ntyux 21.0.0
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/README.md +63 -0
- package/fesm2022/nettyapps-ntyux.mjs +1083 -0
- package/fesm2022/nettyapps-ntyux.mjs.map +1 -0
- package/package.json +23 -0
- package/types/nettyapps-ntyux.d.ts +221 -0
|
@@ -0,0 +1,1083 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Component, NgModule, inject, Injectable, input, output, ViewContainerRef, ViewChild, Input, ViewEncapsulation, signal, effect, computed, forwardRef, ChangeDetectionStrategy, ElementRef, model } from '@angular/core';
|
|
3
|
+
import * as i1$1 from '@angular/material/form-field';
|
|
4
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
5
|
+
import * as i2$1 from '@angular/material/autocomplete';
|
|
6
|
+
import { MatAutocompleteModule, MatAutocompleteTrigger } from '@angular/material/autocomplete';
|
|
7
|
+
import * as i3$1 from '@angular/forms';
|
|
8
|
+
import { ReactiveFormsModule, FormControl, Validators, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
9
|
+
import { ErrorStateMatcher } from '@angular/material/core';
|
|
10
|
+
import * as i4 from '@angular/material/input';
|
|
11
|
+
import { MatInputModule } from '@angular/material/input';
|
|
12
|
+
import * as i2 from '@angular/material/icon';
|
|
13
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
14
|
+
import { UiBase, NettyUISearchInput } from '@nettyapps/ntyui';
|
|
15
|
+
import { HttpClient } from '@angular/common/http';
|
|
16
|
+
import { EnvironmentProxy } from '@nettyapps/ntycontract';
|
|
17
|
+
import { MAT_DIALOG_DATA, MatDialog, MatDialogConfig, MatDialogModule } from '@angular/material/dialog';
|
|
18
|
+
import * as i1 from '@angular/common';
|
|
19
|
+
import { CommonModule, Location, TitleCasePipe } from '@angular/common';
|
|
20
|
+
import * as i3 from '@ngx-translate/core';
|
|
21
|
+
import { TranslateModule } from '@ngx-translate/core';
|
|
22
|
+
import { Clipboard } from '@angular/cdk/clipboard';
|
|
23
|
+
import { AlertService, CommonService, AuthenticationService, NettyMenuService, CredentialsService, NettyImageService } from '@nettyapps/ntybase';
|
|
24
|
+
import * as i5 from '@angular/router';
|
|
25
|
+
import { Router, ActivatedRoute, RouterLink, RouterModule } from '@angular/router';
|
|
26
|
+
import * as i3$2 from '@angular/material/list';
|
|
27
|
+
import { MatListModule } from '@angular/material/list';
|
|
28
|
+
import * as i4$1 from '@angular/material/menu';
|
|
29
|
+
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
|
|
30
|
+
import * as i3$3 from '@angular/material/toolbar';
|
|
31
|
+
import { MatToolbarModule } from '@angular/material/toolbar';
|
|
32
|
+
import { I18nService } from '@nettyapps/ntyi18n';
|
|
33
|
+
|
|
34
|
+
class Ntyux {
|
|
35
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Ntyux, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
36
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.3", type: Ntyux, isStandalone: false, selector: "lib-ntyux", ngImport: i0, template: `
|
|
37
|
+
<p>
|
|
38
|
+
ntyux works!
|
|
39
|
+
</p>
|
|
40
|
+
`, isInline: true, styles: [""] });
|
|
41
|
+
}
|
|
42
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Ntyux, decorators: [{
|
|
43
|
+
type: Component,
|
|
44
|
+
args: [{ selector: 'lib-ntyux', standalone: false, template: `
|
|
45
|
+
<p>
|
|
46
|
+
ntyux works!
|
|
47
|
+
</p>
|
|
48
|
+
` }]
|
|
49
|
+
}] });
|
|
50
|
+
|
|
51
|
+
class NtyuxModule {
|
|
52
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NtyuxModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
53
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.3", ngImport: i0, type: NtyuxModule, declarations: [Ntyux], exports: [Ntyux] });
|
|
54
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NtyuxModule });
|
|
55
|
+
}
|
|
56
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NtyuxModule, decorators: [{
|
|
57
|
+
type: NgModule,
|
|
58
|
+
args: [{
|
|
59
|
+
declarations: [
|
|
60
|
+
Ntyux
|
|
61
|
+
],
|
|
62
|
+
imports: [],
|
|
63
|
+
exports: [
|
|
64
|
+
Ntyux
|
|
65
|
+
]
|
|
66
|
+
}]
|
|
67
|
+
}] });
|
|
68
|
+
|
|
69
|
+
class Guid {
|
|
70
|
+
value = this.empty;
|
|
71
|
+
constructor(value) {
|
|
72
|
+
if (value) {
|
|
73
|
+
if (Guid.isValid(value)) {
|
|
74
|
+
this.value = value;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
static newGuid() {
|
|
79
|
+
return new Guid('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
80
|
+
const r = (Math.random() * 16) | 0;
|
|
81
|
+
const v = c == 'x' ? r : (r & 0x3) | 0x8;
|
|
82
|
+
return v.toString(16);
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* return all zeros '00000000-0000-0000-0000-000000000000'
|
|
87
|
+
*/
|
|
88
|
+
static get empty() {
|
|
89
|
+
return '00000000-0000-0000-0000-000000000000';
|
|
90
|
+
}
|
|
91
|
+
get empty() {
|
|
92
|
+
return Guid.empty;
|
|
93
|
+
}
|
|
94
|
+
static isValid(str) {
|
|
95
|
+
const validRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
96
|
+
return validRegex.test(str);
|
|
97
|
+
}
|
|
98
|
+
toString() {
|
|
99
|
+
return this.value;
|
|
100
|
+
}
|
|
101
|
+
toJSON() {
|
|
102
|
+
return this.value;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* True is guid is empty or not valid
|
|
106
|
+
* @param str
|
|
107
|
+
* @returns
|
|
108
|
+
*/
|
|
109
|
+
static isNullOrEmpty(str) {
|
|
110
|
+
if (str == null || str == undefined) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
if (!Guid.isValid(str)) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
if (str == Guid.empty) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* True if the guid is valid and not all zeros (empty)
|
|
123
|
+
* @param str
|
|
124
|
+
* @returns
|
|
125
|
+
*/
|
|
126
|
+
static isValidAndNotEmpty(str) {
|
|
127
|
+
return !Guid.isNullOrEmpty(str);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Return empty guid if the given guid is not valid
|
|
131
|
+
* @param guid
|
|
132
|
+
* @returns
|
|
133
|
+
*/
|
|
134
|
+
static emptyWhenNull(guid) {
|
|
135
|
+
if (Guid.isValidAndNotEmpty(guid)) {
|
|
136
|
+
return guid;
|
|
137
|
+
}
|
|
138
|
+
return Guid.empty;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
class AutoCompleteProxy {
|
|
143
|
+
http = inject(HttpClient);
|
|
144
|
+
environmentProxy = inject(EnvironmentProxy);
|
|
145
|
+
getAutocompleteDataFilter(searchTable, searchData) {
|
|
146
|
+
let functionUrl = this.environmentProxy.getServerLink(searchTable + '/AutoCompleteFilter');
|
|
147
|
+
return this.http.post(functionUrl, searchData);
|
|
148
|
+
}
|
|
149
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: AutoCompleteProxy, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
150
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: AutoCompleteProxy, providedIn: 'root' });
|
|
151
|
+
}
|
|
152
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: AutoCompleteProxy, decorators: [{
|
|
153
|
+
type: Injectable,
|
|
154
|
+
args: [{
|
|
155
|
+
providedIn: 'root',
|
|
156
|
+
}]
|
|
157
|
+
}] });
|
|
158
|
+
|
|
159
|
+
class NtyAutoCompleteFilter {
|
|
160
|
+
coid = null;
|
|
161
|
+
searchString = null;
|
|
162
|
+
fields = null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* This component can load and display any container which is provided dynamically during run time
|
|
167
|
+
* The only requirement is the component that will be displayed should extend AgGridBaseComponent
|
|
168
|
+
*/
|
|
169
|
+
class AutoCompleteLookup {
|
|
170
|
+
data = inject(MAT_DIALOG_DATA);
|
|
171
|
+
container;
|
|
172
|
+
component = input(...(ngDevMode ? [undefined, { debugName: "component" }] : []));
|
|
173
|
+
filterField = input(null, ...(ngDevMode ? [{ debugName: "filterField" }] : []));
|
|
174
|
+
filterFieldValue = input(null, ...(ngDevMode ? [{ debugName: "filterFieldValue" }] : []));
|
|
175
|
+
filterFieldNumeric = input(false, ...(ngDevMode ? [{ debugName: "filterFieldNumeric" }] : []));
|
|
176
|
+
filterFieldEquality = input('=', ...(ngDevMode ? [{ debugName: "filterFieldEquality" }] : []));
|
|
177
|
+
// Output
|
|
178
|
+
selectedElement = output();
|
|
179
|
+
ngOnInit() {
|
|
180
|
+
this.initializeComponent();
|
|
181
|
+
}
|
|
182
|
+
ngOnChanges(changes) {
|
|
183
|
+
if (this.isComponentConfigChanged(changes)) {
|
|
184
|
+
this.initializeComponent();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
isComponentConfigChanged(changes) {
|
|
188
|
+
return (!!changes['component'] ||
|
|
189
|
+
!!changes['filterField'] ||
|
|
190
|
+
!!changes['filterFieldValue'] ||
|
|
191
|
+
!!changes['filterFieldNumeric'] ||
|
|
192
|
+
!!changes['filterFieldEquality']);
|
|
193
|
+
}
|
|
194
|
+
initializeComponent() {
|
|
195
|
+
const config = this.data || this.getInputConfig();
|
|
196
|
+
this.loadComponent(config);
|
|
197
|
+
}
|
|
198
|
+
getInputConfig() {
|
|
199
|
+
return {
|
|
200
|
+
component: this.component(),
|
|
201
|
+
filterField: this.filterField(),
|
|
202
|
+
filterFieldValue: this.filterFieldValue(),
|
|
203
|
+
filterFieldNumeric: this.filterFieldNumeric(),
|
|
204
|
+
filterFieldEquality: this.filterFieldEquality(),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
loadComponent(config) {
|
|
208
|
+
if (!config?.component)
|
|
209
|
+
return;
|
|
210
|
+
this.container.clear();
|
|
211
|
+
const componentRef = this.container.createComponent(config.component);
|
|
212
|
+
componentRef.setInput('popupValid', true);
|
|
213
|
+
if (config.filterField !== undefined) {
|
|
214
|
+
componentRef.setInput('filterField', config.filterField);
|
|
215
|
+
}
|
|
216
|
+
if (config.filterFieldValue !== undefined) {
|
|
217
|
+
componentRef.setInput('filterFieldValue', config.filterFieldValue);
|
|
218
|
+
}
|
|
219
|
+
if (config.filterFieldEquality !== undefined) {
|
|
220
|
+
componentRef.setInput('filterFieldEquality', config.filterFieldEquality);
|
|
221
|
+
}
|
|
222
|
+
if (config.filterFieldNumeric !== undefined) {
|
|
223
|
+
componentRef.setInput('filterFieldNumeric', config.filterFieldNumeric);
|
|
224
|
+
}
|
|
225
|
+
const sub = componentRef.instance.selectedElement.subscribe((data) => {
|
|
226
|
+
this.selectedElement.emit(data);
|
|
227
|
+
});
|
|
228
|
+
componentRef.onDestroy(() => sub.unsubscribe());
|
|
229
|
+
}
|
|
230
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: AutoCompleteLookup, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
231
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.3", type: AutoCompleteLookup, isStandalone: true, selector: "ntybase-auto-complete-lookup", inputs: { component: { classPropertyName: "component", publicName: "component", isSignal: true, isRequired: false, transformFunction: null }, filterField: { classPropertyName: "filterField", publicName: "filterField", isSignal: true, isRequired: false, transformFunction: null }, filterFieldValue: { classPropertyName: "filterFieldValue", publicName: "filterFieldValue", isSignal: true, isRequired: false, transformFunction: null }, filterFieldNumeric: { classPropertyName: "filterFieldNumeric", publicName: "filterFieldNumeric", isSignal: true, isRequired: false, transformFunction: null }, filterFieldEquality: { classPropertyName: "filterFieldEquality", publicName: "filterFieldEquality", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedElement: "selectedElement" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: ` <ng-template #container></ng-template> `, isInline: true });
|
|
232
|
+
}
|
|
233
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: AutoCompleteLookup, decorators: [{
|
|
234
|
+
type: Component,
|
|
235
|
+
args: [{
|
|
236
|
+
selector: 'ntybase-auto-complete-lookup',
|
|
237
|
+
imports: [],
|
|
238
|
+
template: ` <ng-template #container></ng-template> `,
|
|
239
|
+
}]
|
|
240
|
+
}], propDecorators: { container: [{
|
|
241
|
+
type: ViewChild,
|
|
242
|
+
args: ['container', { read: ViewContainerRef, static: true }]
|
|
243
|
+
}], component: [{ type: i0.Input, args: [{ isSignal: true, alias: "component", required: false }] }], filterField: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterField", required: false }] }], filterFieldValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterFieldValue", required: false }] }], filterFieldNumeric: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterFieldNumeric", required: false }] }], filterFieldEquality: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterFieldEquality", required: false }] }], selectedElement: [{ type: i0.Output, args: ["selectedElement"] }] } });
|
|
244
|
+
|
|
245
|
+
class AutoCompletePopupMenu {
|
|
246
|
+
field = '';
|
|
247
|
+
recordGuid;
|
|
248
|
+
fieldDisabled = false;
|
|
249
|
+
componentPath = '';
|
|
250
|
+
inputValue = '';
|
|
251
|
+
Result = output();
|
|
252
|
+
clipboard = inject(Clipboard);
|
|
253
|
+
alertService = inject(AlertService);
|
|
254
|
+
ngOnInit() { }
|
|
255
|
+
gotoNew() {
|
|
256
|
+
if (!this.componentPath)
|
|
257
|
+
return;
|
|
258
|
+
// Split the component path into segments
|
|
259
|
+
const pathSegments = this.componentPath.split('/').filter((p) => p);
|
|
260
|
+
this.gotoNewTab(pathSegments, '', 'new');
|
|
261
|
+
this.Result.emit('New');
|
|
262
|
+
}
|
|
263
|
+
gotoMain() {
|
|
264
|
+
if (!this.componentPath || !this.recordGuid)
|
|
265
|
+
return;
|
|
266
|
+
// Split the component path into segments
|
|
267
|
+
const pathSegments = this.componentPath.split('/').filter((p) => p);
|
|
268
|
+
this.gotoNewTab(pathSegments, this.recordGuid, 'edit');
|
|
269
|
+
this.Result.emit('Main');
|
|
270
|
+
}
|
|
271
|
+
gotoLookup() {
|
|
272
|
+
if (!this.fieldDisabled) {
|
|
273
|
+
this.Result.emit('Lookup');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
gotoNewTab(target, params, type) {
|
|
277
|
+
if (!target || target.length === 0)
|
|
278
|
+
return;
|
|
279
|
+
// Construct the URL directly in the format you want
|
|
280
|
+
let url = `/${target.join('/')}`;
|
|
281
|
+
// Add query parameters
|
|
282
|
+
const queryParams = [];
|
|
283
|
+
if (params) {
|
|
284
|
+
queryParams.push(`parameters=${encodeURIComponent(JSON.stringify(params))}`);
|
|
285
|
+
}
|
|
286
|
+
if (type) {
|
|
287
|
+
queryParams.push(`type=${type}`);
|
|
288
|
+
}
|
|
289
|
+
queryParams.push('isNewTab=true');
|
|
290
|
+
if (queryParams.length > 0) {
|
|
291
|
+
url += `?${queryParams.join('&')}`;
|
|
292
|
+
}
|
|
293
|
+
// Open in new tab
|
|
294
|
+
window.open(url, '_blank');
|
|
295
|
+
}
|
|
296
|
+
copyToClipboard() {
|
|
297
|
+
if (this.inputValue) {
|
|
298
|
+
this.clipboard.copy(this.inputValue);
|
|
299
|
+
this.alertService.showAlert('@copiedToClipboard');
|
|
300
|
+
this.Result.emit('Copy');
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: AutoCompletePopupMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
304
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.3", type: AutoCompletePopupMenu, isStandalone: true, selector: "ntybase-auto-complete-popup-menu", inputs: { field: "field", recordGuid: "recordGuid", fieldDisabled: "fieldDisabled", componentPath: "componentPath", inputValue: "inputValue" }, outputs: { Result: "Result" }, ngImport: i0, template: "<div class=\"dialogbase\">\n <div class=\"menu\">\n <ul class=\"list-group\">\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center menu-item\"\n >\n <span (click)=\"copyToClipboard()\" class=\"align-line\"\n ><mat-icon>content_copy</mat-icon>{{'@copy'| translate}}</span\n >\n </li>\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center menu-item\"\n >\n <span (click)=\"gotoMain()\" class=\"align-line\"\n ><mat-icon>edit</mat-icon>{{'@popupGotoRecordDefinition'|\n translate}}</span\n >\n </li>\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center menu-item\"\n >\n <span (click)=\"gotoNew()\" class=\"align-line\"\n ><mat-icon>add</mat-icon>{{'@popupNewRecord'| translate}}</span\n >\n </li>\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center\"\n [ngClass]=\"{'menu-item':!fieldDisabled , 'menu-item-disabled':fieldDisabled }\"\n >\n <span (click)=\"gotoLookup()\" class=\"align-line\"\n ><mat-icon>search</mat-icon>{{'@popupSelectFromList'|translate}}</span\n >\n </li>\n </ul>\n </div>\n</div>\n", styles: [".dialogbase{background:#0000001a;padding:0;border-radius:12px;overflow:hidden;box-shadow:0 4px 20px #00000026}.menu{background:var(--mat-sys-primary-container);padding:0;display:flex;border-radius:inherit}.align-line{display:flex;vertical-align:middle;align-items:center;gap:12px;padding:0 8px}.menu-item{padding:12px 16px;display:flex;vertical-align:middle;cursor:pointer;transition:all .2s ease-out;border-radius:8px;margin:4px;color:var(--mat-sys-on-primary-container)}.menu-item:hover{background:var(--mat-sys-on-primary-fixed);color:var(--mat-sys-on-primary);transform:translateY(-2px);box-shadow:0 2px 8px #0000001a}.menu-item:hover .mat-icon{color:var(--mat-sys-on-primary);transform:scale(1.05)}.menu-item-disabled,.menu-item-disabled:hover{background:var(--mat-sys-on-primary-fixed);color:var(--mat-sys-on-primary);font-style:italic;opacity:.7;cursor:not-allowed}.menu-item-disabled .mat-icon,.menu-item-disabled:hover .mat-icon{color:var(--mat-sys-on-primary)}.mat-icon{transition:all .2s ease-out;font-size:20px;width:20px;height:20px}.list-group{list-style:none;padding:8px;margin:0;width:100%}.list-group-item{background-position:bottom;background-size:100% 1px;background-repeat:no-repeat;padding-bottom:12px;margin-bottom:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None });
|
|
305
|
+
}
|
|
306
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: AutoCompletePopupMenu, decorators: [{
|
|
307
|
+
type: Component,
|
|
308
|
+
args: [{ selector: 'ntybase-auto-complete-popup-menu', imports: [CommonModule, ReactiveFormsModule, MatIconModule, TranslateModule], encapsulation: ViewEncapsulation.None, template: "<div class=\"dialogbase\">\n <div class=\"menu\">\n <ul class=\"list-group\">\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center menu-item\"\n >\n <span (click)=\"copyToClipboard()\" class=\"align-line\"\n ><mat-icon>content_copy</mat-icon>{{'@copy'| translate}}</span\n >\n </li>\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center menu-item\"\n >\n <span (click)=\"gotoMain()\" class=\"align-line\"\n ><mat-icon>edit</mat-icon>{{'@popupGotoRecordDefinition'|\n translate}}</span\n >\n </li>\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center menu-item\"\n >\n <span (click)=\"gotoNew()\" class=\"align-line\"\n ><mat-icon>add</mat-icon>{{'@popupNewRecord'| translate}}</span\n >\n </li>\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center\"\n [ngClass]=\"{'menu-item':!fieldDisabled , 'menu-item-disabled':fieldDisabled }\"\n >\n <span (click)=\"gotoLookup()\" class=\"align-line\"\n ><mat-icon>search</mat-icon>{{'@popupSelectFromList'|translate}}</span\n >\n </li>\n </ul>\n </div>\n</div>\n", styles: [".dialogbase{background:#0000001a;padding:0;border-radius:12px;overflow:hidden;box-shadow:0 4px 20px #00000026}.menu{background:var(--mat-sys-primary-container);padding:0;display:flex;border-radius:inherit}.align-line{display:flex;vertical-align:middle;align-items:center;gap:12px;padding:0 8px}.menu-item{padding:12px 16px;display:flex;vertical-align:middle;cursor:pointer;transition:all .2s ease-out;border-radius:8px;margin:4px;color:var(--mat-sys-on-primary-container)}.menu-item:hover{background:var(--mat-sys-on-primary-fixed);color:var(--mat-sys-on-primary);transform:translateY(-2px);box-shadow:0 2px 8px #0000001a}.menu-item:hover .mat-icon{color:var(--mat-sys-on-primary);transform:scale(1.05)}.menu-item-disabled,.menu-item-disabled:hover{background:var(--mat-sys-on-primary-fixed);color:var(--mat-sys-on-primary);font-style:italic;opacity:.7;cursor:not-allowed}.menu-item-disabled .mat-icon,.menu-item-disabled:hover .mat-icon{color:var(--mat-sys-on-primary)}.mat-icon{transition:all .2s ease-out;font-size:20px;width:20px;height:20px}.list-group{list-style:none;padding:8px;margin:0;width:100%}.list-group-item{background-position:bottom;background-size:100% 1px;background-repeat:no-repeat;padding-bottom:12px;margin-bottom:4px}\n"] }]
|
|
309
|
+
}], propDecorators: { field: [{
|
|
310
|
+
type: Input
|
|
311
|
+
}], recordGuid: [{
|
|
312
|
+
type: Input
|
|
313
|
+
}], fieldDisabled: [{
|
|
314
|
+
type: Input
|
|
315
|
+
}], componentPath: [{
|
|
316
|
+
type: Input
|
|
317
|
+
}], inputValue: [{
|
|
318
|
+
type: Input
|
|
319
|
+
}], Result: [{ type: i0.Output, args: ["Result"] }] } });
|
|
320
|
+
|
|
321
|
+
class SelectionItem {
|
|
322
|
+
name = null;
|
|
323
|
+
value = null;
|
|
324
|
+
constructor(_value = null, _name = null) {
|
|
325
|
+
this.name = _name;
|
|
326
|
+
this.value = _value;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
class AutoComplete extends UiBase {
|
|
330
|
+
// Input parameters
|
|
331
|
+
tableName = input('', ...(ngDevMode ? [{ debugName: "tableName" }] : []));
|
|
332
|
+
searchAfter = input(3, ...(ngDevMode ? [{ debugName: "searchAfter" }] : []));
|
|
333
|
+
fieldCode = input(null, ...(ngDevMode ? [{ debugName: "fieldCode" }] : []));
|
|
334
|
+
fieldName = input(null, ...(ngDevMode ? [{ debugName: "fieldName" }] : []));
|
|
335
|
+
lookupComponent = input(null, ...(ngDevMode ? [{ debugName: "lookupComponent" }] : []));
|
|
336
|
+
lookupComponentPath = input(null, ...(ngDevMode ? [{ debugName: "lookupComponentPath" }] : []));
|
|
337
|
+
filterField = input(null, ...(ngDevMode ? [{ debugName: "filterField" }] : []));
|
|
338
|
+
filterFieldValue = input(null, ...(ngDevMode ? [{ debugName: "filterFieldValue" }] : []));
|
|
339
|
+
filterFieldNumeric = input(false, ...(ngDevMode ? [{ debugName: "filterFieldNumeric" }] : []));
|
|
340
|
+
filterFieldEquality = input('=', ...(ngDevMode ? [{ debugName: "filterFieldEquality" }] : []));
|
|
341
|
+
debounceTime = input(500, ...(ngDevMode ? [{ debugName: "debounceTime" }] : []));
|
|
342
|
+
// Inject dependencies
|
|
343
|
+
alertService = inject(AlertService);
|
|
344
|
+
autoCompleteService = inject(AutoCompleteProxy);
|
|
345
|
+
dialog = inject(MatDialog);
|
|
346
|
+
autoCompleteTrigger;
|
|
347
|
+
inputField;
|
|
348
|
+
control = new FormControl();
|
|
349
|
+
matcher = new ErrorStateMatcher();
|
|
350
|
+
// Signal parameters
|
|
351
|
+
searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : []));
|
|
352
|
+
selectedOption = signal(null, ...(ngDevMode ? [{ debugName: "selectedOption" }] : []));
|
|
353
|
+
loadedOptions = signal([], ...(ngDevMode ? [{ debugName: "loadedOptions" }] : []));
|
|
354
|
+
searchSignal = signal('', ...(ngDevMode ? [{ debugName: "searchSignal" }] : []));
|
|
355
|
+
debounceTimeout;
|
|
356
|
+
constructor() {
|
|
357
|
+
super();
|
|
358
|
+
// Sync form control with selected option
|
|
359
|
+
effect(() => {
|
|
360
|
+
const option = this.selectedOption();
|
|
361
|
+
if (option) {
|
|
362
|
+
this.searchText.set(option.name || '');
|
|
363
|
+
}
|
|
364
|
+
else if (this.control.value && !this.selectedOption()) {
|
|
365
|
+
const found = this.loadedOptions().find((opt) => opt.value === this.control.value);
|
|
366
|
+
if (found) {
|
|
367
|
+
this.selectedOption.set(found);
|
|
368
|
+
this.searchText.set(found.name || '');
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
// Then watch the debounced signal:
|
|
373
|
+
effect((onCleanup) => {
|
|
374
|
+
const searchValue = this.searchSignal();
|
|
375
|
+
clearTimeout(this.debounceTimeout);
|
|
376
|
+
this.debounceTimeout = setTimeout(() => {
|
|
377
|
+
if (this.shouldTriggerSearch(searchValue)) {
|
|
378
|
+
this.loadAutoCompleteData(searchValue);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
this.loadedOptions.set([]);
|
|
382
|
+
}
|
|
383
|
+
}, this.debounceTime());
|
|
384
|
+
onCleanup(() => {
|
|
385
|
+
clearTimeout(this.debounceTimeout);
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
effect(() => {
|
|
389
|
+
this.onChange(this.value());
|
|
390
|
+
});
|
|
391
|
+
effect(() => {
|
|
392
|
+
this.updateValidators();
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
shouldTriggerSearch(searchValue) {
|
|
396
|
+
return searchValue.length >= this.searchAfter();
|
|
397
|
+
}
|
|
398
|
+
filteredData = computed(() => {
|
|
399
|
+
return this.loadedOptions();
|
|
400
|
+
}, ...(ngDevMode ? [{ debugName: "filteredData" }] : []));
|
|
401
|
+
onInputChanged(event) {
|
|
402
|
+
const value = event.target.value;
|
|
403
|
+
this.searchText.set(value);
|
|
404
|
+
this.onTouched();
|
|
405
|
+
// Clear selection if text doesn't match
|
|
406
|
+
if (this.selectedOption() && this.selectedOption()?.name !== value) {
|
|
407
|
+
this.selectedOption.set(null);
|
|
408
|
+
}
|
|
409
|
+
if (value.length === 0) {
|
|
410
|
+
this.clearInput();
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
this.searchSignal.set(value);
|
|
414
|
+
this.autoCompleteTrigger.closePanel();
|
|
415
|
+
}
|
|
416
|
+
loadAutoCompleteData(searchValue) {
|
|
417
|
+
this.loadedOptions.set([]);
|
|
418
|
+
const filter = new NtyAutoCompleteFilter();
|
|
419
|
+
filter.searchString =
|
|
420
|
+
searchValue.trim() === '' && searchValue.length >= this.searchAfter()
|
|
421
|
+
? ''
|
|
422
|
+
: searchValue;
|
|
423
|
+
filter.fields = [];
|
|
424
|
+
if (this.filterField() && this.filterField().trim().length > 0) {
|
|
425
|
+
const fields = {
|
|
426
|
+
equalitySign: this.filterFieldEquality(),
|
|
427
|
+
fieldName: this.filterField(),
|
|
428
|
+
fieldValue: this.filterFieldValue(),
|
|
429
|
+
numeric: this.filterFieldNumeric(),
|
|
430
|
+
};
|
|
431
|
+
filter.fields?.push(fields);
|
|
432
|
+
}
|
|
433
|
+
this.autoCompleteService
|
|
434
|
+
.getAutocompleteDataFilter(this.tableName(), filter)
|
|
435
|
+
.subscribe({
|
|
436
|
+
next: (data) => {
|
|
437
|
+
if (data && Array.isArray(data)) {
|
|
438
|
+
const options = data.map((item) => ({
|
|
439
|
+
name: item.text,
|
|
440
|
+
value: item.guidValue,
|
|
441
|
+
}));
|
|
442
|
+
this.loadedOptions.set(options);
|
|
443
|
+
if (options.length === 1) {
|
|
444
|
+
this.selectedOption.set(options[0]);
|
|
445
|
+
this.control.setValue(options[0].value);
|
|
446
|
+
this.searchText.set(options[0].name || '');
|
|
447
|
+
}
|
|
448
|
+
else if (options.length > 1) {
|
|
449
|
+
this.autoCompleteTrigger.openPanel();
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
error: (err) => {
|
|
454
|
+
this.alertService.showAlert(err.message);
|
|
455
|
+
},
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
clearInput() {
|
|
459
|
+
this.selectedOption.set(new SelectionItem(Guid.empty, ''));
|
|
460
|
+
this.control.reset();
|
|
461
|
+
this.searchText.set('');
|
|
462
|
+
this.loadedOptions.set([]);
|
|
463
|
+
this.inputField.nativeElement.value = '';
|
|
464
|
+
}
|
|
465
|
+
onPaste(event) {
|
|
466
|
+
const clipboardData = event.clipboardData;
|
|
467
|
+
if (!clipboardData)
|
|
468
|
+
return;
|
|
469
|
+
const pastedText = clipboardData.getData('text');
|
|
470
|
+
this.searchText.set(pastedText);
|
|
471
|
+
this.searchSignal.set(pastedText);
|
|
472
|
+
}
|
|
473
|
+
displayFn(item) {
|
|
474
|
+
return this.searchText();
|
|
475
|
+
}
|
|
476
|
+
optionSelected(event) {
|
|
477
|
+
const selectedValue = event.option.value;
|
|
478
|
+
const selected = this.loadedOptions().find((opt) => opt.value === selectedValue);
|
|
479
|
+
if (selected) {
|
|
480
|
+
this.selectedOption.set(selected);
|
|
481
|
+
this.control.setValue(selected.value);
|
|
482
|
+
this.searchText.set(selected.name || '');
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
writeValue(value) {
|
|
486
|
+
this.value.set(value);
|
|
487
|
+
if (!value)
|
|
488
|
+
return;
|
|
489
|
+
if (this.fieldCode()) {
|
|
490
|
+
this.selectedOption.set({ value, name: this.fieldCode() });
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
onLookup() {
|
|
494
|
+
if (!this.lookupComponent())
|
|
495
|
+
return;
|
|
496
|
+
const dialogRef = this.dialog.open(AutoCompleteLookup, {
|
|
497
|
+
height: 'auto',
|
|
498
|
+
width: '100%',
|
|
499
|
+
maxWidth: '100%',
|
|
500
|
+
maxHeight: 'auto',
|
|
501
|
+
data: {
|
|
502
|
+
component: this.lookupComponent(),
|
|
503
|
+
...(this.filterField() && {
|
|
504
|
+
filterField: this.filterField(),
|
|
505
|
+
filterFieldValue: this.filterFieldValue(),
|
|
506
|
+
filterFieldEquality: this.filterFieldEquality(),
|
|
507
|
+
filterFieldNumeric: this.filterFieldNumeric(),
|
|
508
|
+
}),
|
|
509
|
+
},
|
|
510
|
+
});
|
|
511
|
+
dialogRef.componentInstance.selectedElement.subscribe((data) => {
|
|
512
|
+
if (data) {
|
|
513
|
+
const selection = new SelectionItem(data.value, data.text);
|
|
514
|
+
this.selectedOption.set(selection);
|
|
515
|
+
this.control.setValue(selection.value);
|
|
516
|
+
this.searchText.set(selection.name || '');
|
|
517
|
+
}
|
|
518
|
+
dialogRef.close();
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
/** Popup screen to goto main table
|
|
522
|
+
*
|
|
523
|
+
* @param event
|
|
524
|
+
* @returns
|
|
525
|
+
*/
|
|
526
|
+
rightClick(event) {
|
|
527
|
+
event.stopPropagation();
|
|
528
|
+
const dialogConfig = new MatDialogConfig();
|
|
529
|
+
dialogConfig.position = {
|
|
530
|
+
top: event.clientY + 'px',
|
|
531
|
+
left: event.clientX + 'px',
|
|
532
|
+
};
|
|
533
|
+
dialogConfig.height = 'auto';
|
|
534
|
+
dialogConfig.width = 'auto';
|
|
535
|
+
dialogConfig.panelClass = 'foo';
|
|
536
|
+
const dialogRef = this.dialog.open(AutoCompletePopupMenu, dialogConfig);
|
|
537
|
+
dialogRef.componentInstance.field = this.tableName();
|
|
538
|
+
dialogRef.componentInstance.recordGuid = this.value();
|
|
539
|
+
dialogRef.componentInstance.fieldDisabled = this.disabled();
|
|
540
|
+
dialogRef.componentInstance.componentPath = this.lookupComponentPath();
|
|
541
|
+
dialogRef.componentInstance.inputValue = this.searchText();
|
|
542
|
+
dialogRef.componentInstance.Result.subscribe((result) => {
|
|
543
|
+
switch (result) {
|
|
544
|
+
case 'Lookup':
|
|
545
|
+
dialogRef.close();
|
|
546
|
+
this.onLookup();
|
|
547
|
+
break;
|
|
548
|
+
default:
|
|
549
|
+
dialogRef.close();
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
// Validation
|
|
556
|
+
getErrorMessage() {
|
|
557
|
+
if (this.control.hasError('required') && this.errorMessages()['required']) {
|
|
558
|
+
return this.errorMessages()['required'];
|
|
559
|
+
}
|
|
560
|
+
return '';
|
|
561
|
+
}
|
|
562
|
+
updateValidators() {
|
|
563
|
+
const validators = [];
|
|
564
|
+
if (this.required()) {
|
|
565
|
+
validators.push(Validators.required);
|
|
566
|
+
}
|
|
567
|
+
this.control.setValidators(validators);
|
|
568
|
+
this.control.updateValueAndValidity();
|
|
569
|
+
}
|
|
570
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: AutoComplete, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
571
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: AutoComplete, isStandalone: true, selector: "ntybase-auto-complete", inputs: { tableName: { classPropertyName: "tableName", publicName: "tableName", isSignal: true, isRequired: false, transformFunction: null }, searchAfter: { classPropertyName: "searchAfter", publicName: "searchAfter", isSignal: true, isRequired: false, transformFunction: null }, fieldCode: { classPropertyName: "fieldCode", publicName: "fieldCode", isSignal: true, isRequired: false, transformFunction: null }, fieldName: { classPropertyName: "fieldName", publicName: "fieldName", isSignal: true, isRequired: false, transformFunction: null }, lookupComponent: { classPropertyName: "lookupComponent", publicName: "lookupComponent", isSignal: true, isRequired: false, transformFunction: null }, lookupComponentPath: { classPropertyName: "lookupComponentPath", publicName: "lookupComponentPath", isSignal: true, isRequired: false, transformFunction: null }, filterField: { classPropertyName: "filterField", publicName: "filterField", isSignal: true, isRequired: false, transformFunction: null }, filterFieldValue: { classPropertyName: "filterFieldValue", publicName: "filterFieldValue", isSignal: true, isRequired: false, transformFunction: null }, filterFieldNumeric: { classPropertyName: "filterFieldNumeric", publicName: "filterFieldNumeric", isSignal: true, isRequired: false, transformFunction: null }, filterFieldEquality: { classPropertyName: "filterFieldEquality", publicName: "filterFieldEquality", isSignal: true, isRequired: false, transformFunction: null }, debounceTime: { classPropertyName: "debounceTime", publicName: "debounceTime", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
572
|
+
{
|
|
573
|
+
provide: NG_VALUE_ACCESSOR,
|
|
574
|
+
useExisting: forwardRef(() => AutoComplete),
|
|
575
|
+
multi: true,
|
|
576
|
+
},
|
|
577
|
+
], viewQueries: [{ propertyName: "autoCompleteTrigger", first: true, predicate: MatAutocompleteTrigger, descendants: true }, { propertyName: "inputField", first: true, predicate: ["autoCompleteInput"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<mat-form-field\n class=\"ui-full-width\"\n [appearance]=\"appearance()\"\n [class.required-field]=\"required()\"\n [class.required-with-value]=\"required() && value()\"\n>\n @if (label()) {\n <mat-label>{{ label() }}</mat-label>\n }\n\n <input\n type=\"text\"\n #autoCompleteInput\n matInput\n [(ngModel)]=\"value\"\n [matAutocomplete]=\"auto\"\n (input)=\"onInputChanged($event)\"\n (paste)=\"onPaste($event)\"\n [errorStateMatcher]=\"matcher\"\n [formControl]=\"control\"\n (contextmenu)=\"rightClick($event)\"\n />\n\n @if (value() && !disabled()) {\n <button\n mat-icon-button\n matSuffix\n (click)=\"clearInput()\"\n aria-label=\"Clear\"\n class=\"clear-btn number-clear-btn\"\n >\n <mat-icon>cancel</mat-icon>\n </button>\n }\n\n <button\n mat-icon-button\n matSuffix\n aria-label=\"Search\"\n (click)=\"onLookup()\"\n class=\"search-btn\"\n >\n <mat-icon>search</mat-icon>\n </button>\n <mat-autocomplete\n #auto=\"matAutocomplete\"\n [displayWith]=\"displayFn.bind(this)\"\n (optionSelected)=\"optionSelected($event)\"\n >\n @for (option of filteredData(); track option.value) {\n <mat-option [value]=\"option.value\"> {{ option.name }} </mat-option>\n }\n </mat-autocomplete>\n\n <mat-error>{{ getErrorMessage() }}</mat-error>\n</mat-form-field>\n", styles: ["::ng-deep .ui-full-width{width:100%;max-width:500px}::ng-deep .mat-mdc-form-field-subscript-wrapper{width:auto;height:0}::ng-deep .clear-btn{background:none;border:none;box-shadow:none;cursor:pointer}::ng-deep .search-icon{background:none;border:none;box-shadow:none;opacity:1;cursor:pointer}::ng-deep .search-icon:hover,::ng-deep .clear-btn:hover{color:#f97a00}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i1$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i1$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i2$1.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i2$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i2$1.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatDialogModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
578
|
+
}
|
|
579
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: AutoComplete, decorators: [{
|
|
580
|
+
type: Component,
|
|
581
|
+
args: [{ selector: 'ntybase-auto-complete', imports: [
|
|
582
|
+
MatFormFieldModule,
|
|
583
|
+
MatAutocompleteModule,
|
|
584
|
+
FormsModule,
|
|
585
|
+
MatInputModule,
|
|
586
|
+
MatIconModule,
|
|
587
|
+
ReactiveFormsModule,
|
|
588
|
+
MatDialogModule,
|
|
589
|
+
], providers: [
|
|
590
|
+
{
|
|
591
|
+
provide: NG_VALUE_ACCESSOR,
|
|
592
|
+
useExisting: forwardRef(() => AutoComplete),
|
|
593
|
+
multi: true,
|
|
594
|
+
},
|
|
595
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<mat-form-field\n class=\"ui-full-width\"\n [appearance]=\"appearance()\"\n [class.required-field]=\"required()\"\n [class.required-with-value]=\"required() && value()\"\n>\n @if (label()) {\n <mat-label>{{ label() }}</mat-label>\n }\n\n <input\n type=\"text\"\n #autoCompleteInput\n matInput\n [(ngModel)]=\"value\"\n [matAutocomplete]=\"auto\"\n (input)=\"onInputChanged($event)\"\n (paste)=\"onPaste($event)\"\n [errorStateMatcher]=\"matcher\"\n [formControl]=\"control\"\n (contextmenu)=\"rightClick($event)\"\n />\n\n @if (value() && !disabled()) {\n <button\n mat-icon-button\n matSuffix\n (click)=\"clearInput()\"\n aria-label=\"Clear\"\n class=\"clear-btn number-clear-btn\"\n >\n <mat-icon>cancel</mat-icon>\n </button>\n }\n\n <button\n mat-icon-button\n matSuffix\n aria-label=\"Search\"\n (click)=\"onLookup()\"\n class=\"search-btn\"\n >\n <mat-icon>search</mat-icon>\n </button>\n <mat-autocomplete\n #auto=\"matAutocomplete\"\n [displayWith]=\"displayFn.bind(this)\"\n (optionSelected)=\"optionSelected($event)\"\n >\n @for (option of filteredData(); track option.value) {\n <mat-option [value]=\"option.value\"> {{ option.name }} </mat-option>\n }\n </mat-autocomplete>\n\n <mat-error>{{ getErrorMessage() }}</mat-error>\n</mat-form-field>\n", styles: ["::ng-deep .ui-full-width{width:100%;max-width:500px}::ng-deep .mat-mdc-form-field-subscript-wrapper{width:auto;height:0}::ng-deep .clear-btn{background:none;border:none;box-shadow:none;cursor:pointer}::ng-deep .search-icon{background:none;border:none;box-shadow:none;opacity:1;cursor:pointer}::ng-deep .search-icon:hover,::ng-deep .clear-btn:hover{color:#f97a00}\n"] }]
|
|
596
|
+
}], ctorParameters: () => [], propDecorators: { tableName: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableName", required: false }] }], searchAfter: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchAfter", required: false }] }], fieldCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldCode", required: false }] }], fieldName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldName", required: false }] }], lookupComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "lookupComponent", required: false }] }], lookupComponentPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "lookupComponentPath", required: false }] }], filterField: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterField", required: false }] }], filterFieldValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterFieldValue", required: false }] }], filterFieldNumeric: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterFieldNumeric", required: false }] }], filterFieldEquality: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterFieldEquality", required: false }] }], debounceTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounceTime", required: false }] }], autoCompleteTrigger: [{
|
|
597
|
+
type: ViewChild,
|
|
598
|
+
args: [MatAutocompleteTrigger]
|
|
599
|
+
}], inputField: [{
|
|
600
|
+
type: ViewChild,
|
|
601
|
+
args: ['autoCompleteInput']
|
|
602
|
+
}] } });
|
|
603
|
+
|
|
604
|
+
class HttpError403 {
|
|
605
|
+
attemptedUrl = '';
|
|
606
|
+
routeSubscription;
|
|
607
|
+
router = inject(Router);
|
|
608
|
+
route = inject(ActivatedRoute);
|
|
609
|
+
location = inject(Location);
|
|
610
|
+
commonService = inject(CommonService);
|
|
611
|
+
constructor() {
|
|
612
|
+
// Router'dan state'i al
|
|
613
|
+
const navigation = this.router.currentNavigation();
|
|
614
|
+
this.attemptedUrl = navigation?.extras.state?.['attemptedUrl'] || '';
|
|
615
|
+
// Eğer state'ten gelmediyse, mevcut URL'i temizleyerek kullan
|
|
616
|
+
if (!this.attemptedUrl) {
|
|
617
|
+
this.attemptedUrl = this.getCleanUrlPath();
|
|
618
|
+
}
|
|
619
|
+
this.routeSubscription = this.router.events.subscribe(() => {
|
|
620
|
+
this.updateAttemptedUrl();
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
ngOnInit() {
|
|
624
|
+
// 1. URL'deki secondary outlet'leri temizle (örneğin: "(rightSidenav:...)")
|
|
625
|
+
this.cleanUrlFromFragments();
|
|
626
|
+
// 2. Hata mesajı için attemptedUrl'i güncelle
|
|
627
|
+
this.updateAttemptedUrl();
|
|
628
|
+
// 3. Right sidenav'ı kapat
|
|
629
|
+
this.commonService.toggleRightSidenav(false);
|
|
630
|
+
}
|
|
631
|
+
ngOnDestroy() {
|
|
632
|
+
if (this.routeSubscription) {
|
|
633
|
+
this.routeSubscription.unsubscribe();
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* URL'deki fragment ve outlet'leri temizler.
|
|
638
|
+
* Örnek: "/sys/NettyUserGroup(rightSidenav:...)" → "/sys/NettyUserGroup"
|
|
639
|
+
*/
|
|
640
|
+
cleanUrlFromFragments() {
|
|
641
|
+
const currentUrl = this.router.url;
|
|
642
|
+
const cleanUrl = currentUrl.split('(')[0]; // "(" sonrasını sil
|
|
643
|
+
if (currentUrl !== cleanUrl) {
|
|
644
|
+
// URL'yi temizlenmiş haliyle değiştir (tarayıcı geçmişine eklemeden)
|
|
645
|
+
this.router.navigateByUrl(cleanUrl, {
|
|
646
|
+
replaceUrl: true,
|
|
647
|
+
skipLocationChange: false,
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* attemptedUrl'i güncel router URL'ine göre günceller
|
|
653
|
+
*/
|
|
654
|
+
updateAttemptedUrl() {
|
|
655
|
+
this.attemptedUrl = this.getCleanUrlPath();
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Temizlenmiş URL yolunu döndürür (queryParams ve fragment olmadan).
|
|
659
|
+
*/
|
|
660
|
+
getCleanUrlPath() {
|
|
661
|
+
return this.router.url.split('?')[0].split('(')[0];
|
|
662
|
+
}
|
|
663
|
+
goHome() {
|
|
664
|
+
this.router.navigate(['/'], { replaceUrl: true });
|
|
665
|
+
this.commonService.toggleRightSidenav(false);
|
|
666
|
+
}
|
|
667
|
+
goBack() {
|
|
668
|
+
this.location.back();
|
|
669
|
+
}
|
|
670
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: HttpError403, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
671
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.3", type: HttpError403, isStandalone: true, selector: "ntybase-http-error403", ngImport: i0, template: "<div class=\"error-container\">\n <div class=\"error-page\">\n <h1>403 - Eri\u015Fim Engellendi</h1>\n\n <p>\n <strong>\u00DCzg\u00FCn\u00FCz, {{ attemptedUrl }}</strong> sayfas\u0131na eri\u015Fim izniniz\n bulunmamaktad\u0131r.\n </p>\n\n <p class=\"error-detail\">\n Bu sayfay\u0131 g\u00F6r\u00FCnt\u00FClemek i\u00E7in gerekli yetkilere sahip de\u011Filsiniz. E\u011Fer bu\n bir hata oldu\u011Funu d\u00FC\u015F\u00FCn\u00FCyorsan\u0131z, l\u00FCtfen sistem y\u00F6neticinizle ileti\u015Fime\n ge\u00E7in.\n </p>\n <button mat-raised-button (click)=\"goHome()\" color=\"primary\" routerLink=\"/\">\n Ana Sayfaya D\u00F6n\n </button>\n <button mat-raised-button (click)=\"goBack()\" color=\"primary\" routerLink=\"/\">\n Geri D\u00F6n\n </button>\n </div>\n</div>\n", styles: [".error-container{background-image:var(--httperror403-bg-image, linear-gradient(rgba(0, 0, 0, .6), rgba(0, 0, 0, .6)), url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-blend-mode:overlay;height:100vh;width:100%;background-position:center;background-repeat:no-repeat;background-size:cover;display:flex;justify-content:center;align-items:center;color:#fff;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.error-page{text-align:center;padding:3rem;max-width:650px;margin:0 auto;background-color:#000000b3;border-radius:16px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 8px 32px #0000004d;animation:fadeIn .8s ease-in-out}.error-page h1{font-size:6rem;margin:0;color:var(--mat-sys-primary-container);text-shadow:0 4px 8px rgba(0,0,0,.3);font-weight:700}.error-page h3{font-size:1.8rem;margin-top:0;margin-bottom:1.5rem;color:#f8f9fa}.error-page p{margin:1.5rem 0;font-size:1.1rem;line-height:1.6;color:#e9ecef}.error-page strong{word-break:break-all;color:#ff6b6b;font-weight:500}button{margin:.5rem;padding:.8rem 2rem;font-size:1rem;font-weight:500;border-radius:50px;transition:all .3s ease;background-color:var(--mat-sys-primary);color:var(--mat-sys-on-primary)}button:hover{transform:translateY(-2px);box-shadow:0 6px 12px #00000026}button:active{transform:translateY(0)}@keyframes fadeIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media(max-width:600px){.error-page{padding:2rem;width:90%}.error-page h1{font-size:4rem}.error-page h3{font-size:1.5rem}button{display:block;width:100%;margin:.5rem 0}}\n"] });
|
|
672
|
+
}
|
|
673
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: HttpError403, decorators: [{
|
|
674
|
+
type: Component,
|
|
675
|
+
args: [{ selector: 'ntybase-http-error403', imports: [], template: "<div class=\"error-container\">\n <div class=\"error-page\">\n <h1>403 - Eri\u015Fim Engellendi</h1>\n\n <p>\n <strong>\u00DCzg\u00FCn\u00FCz, {{ attemptedUrl }}</strong> sayfas\u0131na eri\u015Fim izniniz\n bulunmamaktad\u0131r.\n </p>\n\n <p class=\"error-detail\">\n Bu sayfay\u0131 g\u00F6r\u00FCnt\u00FClemek i\u00E7in gerekli yetkilere sahip de\u011Filsiniz. E\u011Fer bu\n bir hata oldu\u011Funu d\u00FC\u015F\u00FCn\u00FCyorsan\u0131z, l\u00FCtfen sistem y\u00F6neticinizle ileti\u015Fime\n ge\u00E7in.\n </p>\n <button mat-raised-button (click)=\"goHome()\" color=\"primary\" routerLink=\"/\">\n Ana Sayfaya D\u00F6n\n </button>\n <button mat-raised-button (click)=\"goBack()\" color=\"primary\" routerLink=\"/\">\n Geri D\u00F6n\n </button>\n </div>\n</div>\n", styles: [".error-container{background-image:var(--httperror403-bg-image, linear-gradient(rgba(0, 0, 0, .6), rgba(0, 0, 0, .6)), url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-blend-mode:overlay;height:100vh;width:100%;background-position:center;background-repeat:no-repeat;background-size:cover;display:flex;justify-content:center;align-items:center;color:#fff;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.error-page{text-align:center;padding:3rem;max-width:650px;margin:0 auto;background-color:#000000b3;border-radius:16px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 8px 32px #0000004d;animation:fadeIn .8s ease-in-out}.error-page h1{font-size:6rem;margin:0;color:var(--mat-sys-primary-container);text-shadow:0 4px 8px rgba(0,0,0,.3);font-weight:700}.error-page h3{font-size:1.8rem;margin-top:0;margin-bottom:1.5rem;color:#f8f9fa}.error-page p{margin:1.5rem 0;font-size:1.1rem;line-height:1.6;color:#e9ecef}.error-page strong{word-break:break-all;color:#ff6b6b;font-weight:500}button{margin:.5rem;padding:.8rem 2rem;font-size:1rem;font-weight:500;border-radius:50px;transition:all .3s ease;background-color:var(--mat-sys-primary);color:var(--mat-sys-on-primary)}button:hover{transform:translateY(-2px);box-shadow:0 6px 12px #00000026}button:active{transform:translateY(0)}@keyframes fadeIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media(max-width:600px){.error-page{padding:2rem;width:90%}.error-page h1{font-size:4rem}.error-page h3{font-size:1.5rem}button{display:block;width:100%;margin:.5rem 0}}\n"] }]
|
|
676
|
+
}], ctorParameters: () => [] });
|
|
677
|
+
|
|
678
|
+
class HttpError404 {
|
|
679
|
+
attemptedUrl = '';
|
|
680
|
+
routeSubscription;
|
|
681
|
+
router = inject(Router);
|
|
682
|
+
route = inject(ActivatedRoute);
|
|
683
|
+
location = inject(Location);
|
|
684
|
+
commonService = inject(CommonService);
|
|
685
|
+
constructor() {
|
|
686
|
+
this.routeSubscription = this.router.events.subscribe(() => {
|
|
687
|
+
this.updateAttemptedUrl();
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
ngOnInit() {
|
|
691
|
+
// 1. URL'deki secondary outlet'leri temizle (örneğin: "(rightSidenav:...)")
|
|
692
|
+
this.cleanUrlFromFragments();
|
|
693
|
+
// 2. Hata mesajı için attemptedUrl'i güncelle
|
|
694
|
+
this.updateAttemptedUrl();
|
|
695
|
+
// 3. Right sidenav'ı kapat
|
|
696
|
+
this.commonService.toggleRightSidenav(false);
|
|
697
|
+
}
|
|
698
|
+
ngOnDestroy() {
|
|
699
|
+
if (this.routeSubscription) {
|
|
700
|
+
this.routeSubscription.unsubscribe();
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* URL'deki fragment ve outlet'leri temizler.
|
|
705
|
+
* Örnek: "/sys/NettyUserGroup(rightSidenav:...)" → "/sys/NettyUserGroup"
|
|
706
|
+
*/
|
|
707
|
+
cleanUrlFromFragments() {
|
|
708
|
+
const currentUrl = this.router.url;
|
|
709
|
+
const cleanUrl = currentUrl.split('(')[0]; // "(" sonrasını sil
|
|
710
|
+
if (currentUrl !== cleanUrl) {
|
|
711
|
+
// URL'yi temizlenmiş haliyle değiştir (tarayıcı geçmişine eklemeden)
|
|
712
|
+
this.router.navigateByUrl(cleanUrl, {
|
|
713
|
+
replaceUrl: true,
|
|
714
|
+
skipLocationChange: false,
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Temizlenmiş URL yolunu döndürür (queryParams ve fragment olmadan).
|
|
720
|
+
*/
|
|
721
|
+
getCleanUrlPath() {
|
|
722
|
+
return this.router.url.split('?')[0].split('(')[0];
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* attemptedUrl'i güncel router URL'ine göre günceller
|
|
726
|
+
*/
|
|
727
|
+
updateAttemptedUrl() {
|
|
728
|
+
this.attemptedUrl = this.getCleanUrlPath();
|
|
729
|
+
}
|
|
730
|
+
goHome() {
|
|
731
|
+
this.router.navigate(['/'], { replaceUrl: true });
|
|
732
|
+
this.commonService.toggleRightSidenav(false);
|
|
733
|
+
}
|
|
734
|
+
goBack() {
|
|
735
|
+
this.location.back();
|
|
736
|
+
}
|
|
737
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: HttpError404, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
738
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.3", type: HttpError404, isStandalone: true, selector: "ntybase-http-error404", ngImport: i0, template: "<div class=\"error-container\">\n <div class=\"error-page\">\n <h1>404</h1>\n <h3>Sayfa Bulunamad\u0131</h3>\n <p><strong>{{ attemptedUrl }}</strong> adresi bulunamad\u0131.</p>\n <p>Bu i\u00E7erik ta\u015F\u0131nm\u0131\u015F veya silinmi\u015F olabilir.</p>\n <button mat-raised-button (click)=\"goHome()\" color=\"primary\" routerLink=\"/\">\n Ana Sayfaya D\u00F6n\n </button>\n <button mat-raised-button (click)=\"goBack()\" color=\"primary\" routerLink=\"/\">\n Geri D\u00F6n\n </button>\n </div>\n</div>\n", styles: [".error-container{background-image:var(--httperror404-bg-image, linear-gradient(rgba(0, 0, 0, .6), rgba(0, 0, 0, .6)), url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-blend-mode:overlay;height:100vh;width:100%;background-position:center;background-repeat:no-repeat;background-size:cover;display:flex;justify-content:center;align-items:center;color:#fff;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.error-page{text-align:center;padding:3rem;max-width:650px;margin:0 auto;background-color:#000000b3;border-radius:16px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 8px 32px #0000004d;animation:fadeIn .8s ease-in-out}.error-page h1{font-size:6rem;margin:0;color:var(--mat-sys-primary-container);text-shadow:0 4px 8px rgba(0,0,0,.3);font-weight:700}.error-page h3{font-size:1.8rem;margin-top:0;margin-bottom:1.5rem;color:#f8f9fa}.error-page p{margin:1.5rem 0;font-size:1.1rem;line-height:1.6;color:#e9ecef}.error-page strong{word-break:break-all;color:#ff6b6b;font-weight:500}button{margin:.5rem;padding:.8rem 2rem;font-size:1rem;font-weight:500;border-radius:50px;transition:all .3s ease;background-color:var(--mat-sys-primary);color:var(--mat-sys-on-primary)}button:hover{transform:translateY(-2px);box-shadow:0 6px 12px #00000026}button:active{transform:translateY(0)}@keyframes fadeIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media(max-width:600px){.error-page{padding:2rem;width:90%}.error-page h1{font-size:4rem}.error-page h3{font-size:1.5rem}button{display:block;width:100%;margin:.5rem 0}}\n"] });
|
|
739
|
+
}
|
|
740
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: HttpError404, decorators: [{
|
|
741
|
+
type: Component,
|
|
742
|
+
args: [{ selector: 'ntybase-http-error404', imports: [], template: "<div class=\"error-container\">\n <div class=\"error-page\">\n <h1>404</h1>\n <h3>Sayfa Bulunamad\u0131</h3>\n <p><strong>{{ attemptedUrl }}</strong> adresi bulunamad\u0131.</p>\n <p>Bu i\u00E7erik ta\u015F\u0131nm\u0131\u015F veya silinmi\u015F olabilir.</p>\n <button mat-raised-button (click)=\"goHome()\" color=\"primary\" routerLink=\"/\">\n Ana Sayfaya D\u00F6n\n </button>\n <button mat-raised-button (click)=\"goBack()\" color=\"primary\" routerLink=\"/\">\n Geri D\u00F6n\n </button>\n </div>\n</div>\n", styles: [".error-container{background-image:var(--httperror404-bg-image, linear-gradient(rgba(0, 0, 0, .6), rgba(0, 0, 0, .6)), url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-blend-mode:overlay;height:100vh;width:100%;background-position:center;background-repeat:no-repeat;background-size:cover;display:flex;justify-content:center;align-items:center;color:#fff;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.error-page{text-align:center;padding:3rem;max-width:650px;margin:0 auto;background-color:#000000b3;border-radius:16px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 8px 32px #0000004d;animation:fadeIn .8s ease-in-out}.error-page h1{font-size:6rem;margin:0;color:var(--mat-sys-primary-container);text-shadow:0 4px 8px rgba(0,0,0,.3);font-weight:700}.error-page h3{font-size:1.8rem;margin-top:0;margin-bottom:1.5rem;color:#f8f9fa}.error-page p{margin:1.5rem 0;font-size:1.1rem;line-height:1.6;color:#e9ecef}.error-page strong{word-break:break-all;color:#ff6b6b;font-weight:500}button{margin:.5rem;padding:.8rem 2rem;font-size:1rem;font-weight:500;border-radius:50px;transition:all .3s ease;background-color:var(--mat-sys-primary);color:var(--mat-sys-on-primary)}button:hover{transform:translateY(-2px);box-shadow:0 6px 12px #00000026}button:active{transform:translateY(0)}@keyframes fadeIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media(max-width:600px){.error-page{padding:2rem;width:90%}.error-page h1{font-size:4rem}.error-page h3{font-size:1.5rem}button{display:block;width:100%;margin:.5rem 0}}\n"] }]
|
|
743
|
+
}], ctorParameters: () => [] });
|
|
744
|
+
|
|
745
|
+
class LeftSidenav {
|
|
746
|
+
authService = inject(AuthenticationService);
|
|
747
|
+
router = inject(Router);
|
|
748
|
+
menuService = inject(NettyMenuService);
|
|
749
|
+
commonService = inject(CommonService);
|
|
750
|
+
environmentProxy = inject(EnvironmentProxy);
|
|
751
|
+
credentialsService = inject(CredentialsService);
|
|
752
|
+
imageService = inject(NettyImageService);
|
|
753
|
+
elementRef = inject(ElementRef);
|
|
754
|
+
username = signal('', ...(ngDevMode ? [{ debugName: "username" }] : []));
|
|
755
|
+
profileImage = signal('', ...(ngDevMode ? [{ debugName: "profileImage" }] : []));
|
|
756
|
+
version = this.environmentProxy.version();
|
|
757
|
+
isMinimized = input(false, ...(ngDevMode ? [{ debugName: "isMinimized" }] : []));
|
|
758
|
+
toggleMinimize = output();
|
|
759
|
+
onToggleMinimize() {
|
|
760
|
+
this.toggleMinimize.emit();
|
|
761
|
+
}
|
|
762
|
+
logout() {
|
|
763
|
+
this.authService.logout().subscribe({
|
|
764
|
+
next: (success) => {
|
|
765
|
+
if (success) {
|
|
766
|
+
const cleanUrl = this.commonService.getCleanUrlPath();
|
|
767
|
+
this.router.navigateByUrl(cleanUrl, { replaceUrl: true }).then(() => {
|
|
768
|
+
this.router.navigate(['/login']);
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
error: (err) => {
|
|
773
|
+
console.error('Logout failed:', err);
|
|
774
|
+
},
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
// Menu Search
|
|
778
|
+
menuItems = signal([], ...(ngDevMode ? [{ debugName: "menuItems" }] : []));
|
|
779
|
+
searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
|
|
780
|
+
originalMenuItems = signal([], ...(ngDevMode ? [{ debugName: "originalMenuItems" }] : []));
|
|
781
|
+
constructor() {
|
|
782
|
+
effect(() => {
|
|
783
|
+
const fetchedMenu = this.menuService.menu();
|
|
784
|
+
if (fetchedMenu) {
|
|
785
|
+
const processedItems = this.processMenuItems(fetchedMenu);
|
|
786
|
+
this.originalMenuItems.set(processedItems);
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
this.originalMenuItems.set([]);
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
this.loadUserInfo();
|
|
793
|
+
}
|
|
794
|
+
ngOnInit() {
|
|
795
|
+
// Application name gelene kadar 3 kez dene
|
|
796
|
+
this.trySetMenuName(0);
|
|
797
|
+
}
|
|
798
|
+
trySetMenuName(attempt) {
|
|
799
|
+
const appName = this.environmentProxy.getApplicationName();
|
|
800
|
+
if (appName && appName.trim()) {
|
|
801
|
+
// Application name geldi, menüyü yükle
|
|
802
|
+
this.menuService.setMenuName(appName);
|
|
803
|
+
}
|
|
804
|
+
else if (attempt < 3) {
|
|
805
|
+
// Henüz gelmedi, 300ms sonra tekrar dene
|
|
806
|
+
setTimeout(() => {
|
|
807
|
+
this.trySetMenuName(attempt + 1);
|
|
808
|
+
}, 300);
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
// 3 denemeden sonra hala gelmedi, boş string ile dene
|
|
812
|
+
console.warn('Application name not found, using empty string');
|
|
813
|
+
this.menuService.setMenuName('');
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
ngAfterViewInit() {
|
|
817
|
+
setTimeout(() => {
|
|
818
|
+
this.loadUserInfo();
|
|
819
|
+
}, 0);
|
|
820
|
+
}
|
|
821
|
+
loadUserInfo() {
|
|
822
|
+
const credentials = this.credentialsService.getCredentials();
|
|
823
|
+
if (credentials && credentials.username) {
|
|
824
|
+
this.username.set(credentials.username);
|
|
825
|
+
this.generateProfileImage(credentials.username);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
generateProfileImage(username) {
|
|
829
|
+
const initials = this.getInitials(username);
|
|
830
|
+
const primaryColor = this.getCssVariableValue('--mat-sys-primary');
|
|
831
|
+
const imageUrl = this.imageService.generateTextImage(initials, 40, 40, primaryColor || '#4ECDC4', '#FFFFFF', 'rgba(0, 0, 0, 0.2)');
|
|
832
|
+
this.profileImage.set(imageUrl);
|
|
833
|
+
}
|
|
834
|
+
getCssVariableValue(variableName) {
|
|
835
|
+
if (typeof window === 'undefined')
|
|
836
|
+
return null;
|
|
837
|
+
const element = this.elementRef.nativeElement;
|
|
838
|
+
return (getComputedStyle(element).getPropertyValue(variableName).trim() || null);
|
|
839
|
+
}
|
|
840
|
+
getInitials(username) {
|
|
841
|
+
if (!username)
|
|
842
|
+
return 'U';
|
|
843
|
+
const names = username.trim().split(' ');
|
|
844
|
+
if (names.length === 1) {
|
|
845
|
+
return names[0].charAt(0).toUpperCase();
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
return (names[0].charAt(0) + names[names.length - 1].charAt(0)).toUpperCase();
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
processMenuItems(apiResponse) {
|
|
852
|
+
if (!apiResponse || !Array.isArray(apiResponse)) {
|
|
853
|
+
return [];
|
|
854
|
+
}
|
|
855
|
+
return apiResponse.map((item) => ({
|
|
856
|
+
name: item.label || item.name,
|
|
857
|
+
icon: item.icon || '',
|
|
858
|
+
link: item.route || item.link || '#',
|
|
859
|
+
expanded: false,
|
|
860
|
+
children: item.children ? this.processMenuItems(item.children) : [],
|
|
861
|
+
}));
|
|
862
|
+
}
|
|
863
|
+
filteredMenuItems = computed(() => {
|
|
864
|
+
const term = this.searchTerm().toLowerCase();
|
|
865
|
+
if (!term)
|
|
866
|
+
return this.originalMenuItems();
|
|
867
|
+
return this.filterMenuItems(this.originalMenuItems(), term);
|
|
868
|
+
}, ...(ngDevMode ? [{ debugName: "filteredMenuItems" }] : []));
|
|
869
|
+
filterMenuItems(items, term) {
|
|
870
|
+
const normalizedTerm = this.commonService.normalizeTurkish(term);
|
|
871
|
+
return items
|
|
872
|
+
.map((item) => {
|
|
873
|
+
const filteredChildren = item.children
|
|
874
|
+
? this.filterMenuItems(item.children, term)
|
|
875
|
+
: [];
|
|
876
|
+
const normalizedName = this.commonService.normalizeTurkish(item.name);
|
|
877
|
+
const matches = normalizedName.includes(normalizedTerm) ||
|
|
878
|
+
filteredChildren.length > 0;
|
|
879
|
+
return matches
|
|
880
|
+
? {
|
|
881
|
+
...item,
|
|
882
|
+
children: filteredChildren,
|
|
883
|
+
expanded: matches || filteredChildren.length > 0,
|
|
884
|
+
}
|
|
885
|
+
: null;
|
|
886
|
+
})
|
|
887
|
+
.filter((item) => item !== null);
|
|
888
|
+
}
|
|
889
|
+
toggleSubMenu(item) {
|
|
890
|
+
item.expanded = !item.expanded;
|
|
891
|
+
}
|
|
892
|
+
onSearch(term) {
|
|
893
|
+
this.searchTerm.set(term);
|
|
894
|
+
}
|
|
895
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: LeftSidenav, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
896
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: LeftSidenav, isStandalone: true, selector: "ntybase-left-sidenav", inputs: { isMinimized: { classPropertyName: "isMinimized", publicName: "isMinimized", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleMinimize: "toggleMinimize" }, ngImport: i0, template: "<div class=\"sidenav-content-wrapper\">\n <!-- Minimize Icon -->\n <button mat-icon-button (click)=\"onToggleMinimize()\" class=\"minimize-button\">\n <mat-icon class=\"minimize-icon\">\n {{ isMinimized() ? \"chevron_right\" : \"chevron_left\" }}\n </mat-icon>\n </button>\n\n <!-- Profile -->\n <div class=\"profile-section\">\n <button mat-button [matMenuTriggerFor]=\"profileMenu\" class=\"profile-button\">\n <img [src]=\"profileImage()\" [alt]=\"username()\" class=\"profile-image\" />\n @if(!isMinimized()){\n <div class=\"profile-info\">\n <p class=\"profile-name\">{{ username() }}</p>\n </div>\n }\n </button>\n\n <mat-menu #profileMenu=\"matMenu\" xPosition=\"before\">\n <button mat-menu-item disabled>\n <mat-icon>account_circle</mat-icon>\n <span>{{ '@profile' | translate }}</span>\n </button>\n <button mat-menu-item mat-button disabled>\n <mat-icon>swap_horiz</mat-icon>\n <span>{{ '@changeCompany' | translate }}</span>\n </button>\n <button mat-menu-item mat-button (click)=\"logout()\">\n <mat-icon>logout</mat-icon>\n <span>{{ '@logout' | translate }}</span>\n </button>\n </mat-menu>\n </div>\n\n <!-- Search Input -->\n @if (!isMinimized()){\n <ntyui-search-input\n class=\"search-input\"\n [label]=\"'@search' | translate\"\n [placeholder]=\"'@placeholderSearch' | translate\"\n [appearance]=\"'outline'\"\n (search)=\"onSearch($event)\"\n >\n </ntyui-search-input>\n }\n\n <!-- Menu -->\n @if (!isMinimized()) {\n <mat-nav-list>\n <div class=\"sidebar\">\n <ul>\n <!-- Recursive menu render -->\n @let items = filteredMenuItems(); @let level = 0;\n <ng-container\n [ngTemplateOutlet]=\"menuItemsTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: items, level: level }\"\n ></ng-container>\n </ul>\n </div>\n </mat-nav-list>\n }\n\n <!-- Footer -->\n <div class=\"sidenav-footer\">\n <button mat-button [matMenuTriggerFor]=\"footerMenu\" class=\"footer-button\">\n @if (!isMinimized()) {\n <div class=\"footer-text\">\n <div class=\"company-name\">\n <p>© 2025 AXIS</p>\n </div>\n </div>\n }\n </button>\n <div class=\"version\">\n <span class=\"version\">v{{version}}</span>\n </div>\n </div>\n\n <mat-menu #footerMenu=\"matMenu\" xPosition=\"before\">\n <button mat-menu-item disabled>\n <mat-icon>swap_horiz</mat-icon>\n {{ '@changeCompany' | translate }}\n </button>\n <button mat-menu-item mat-button (click)=\"logout()\">\n <mat-icon>logout</mat-icon>\n <span>{{ '@logout' | translate }}</span>\n </button>\n </mat-menu>\n</div>\n\n<!-- Recursive Menu Template -->\n<ng-template #menuItemsTemplate let-items let-level=\"level\">\n @for (item of items; track item.name) {\n <li [class]=\"'menu-level-' + level\">\n <!-- Leaf node (no children) -->\n @if (!item.children || item.children.length === 0) {\n <a\n [routerLink]=\"item.link\"\n routerLinkActive=\"active\"\n [class]=\"'menu-link level-' + level\"\n >\n <mat-icon>{{ item.icon }}</mat-icon>\n <span>{{ item.name }}</span>\n </a>\n }\n\n <!-- Parent node with children -->\n @if (item.children && item.children.length > 0) {\n <a\n (click)=\"toggleSubMenu(item)\"\n [class.expanded]=\"item.expanded\"\n [class]=\"'menu-link has-children level-' + level\"\n >\n <mat-icon>{{ item.icon }}</mat-icon>\n <span>{{ item.name }}</span>\n <mat-icon [class.rotated]=\"item.expanded\" class=\"expand-icon\">\n {{ item.expanded ? 'expand_less' : 'expand_more' }}\n </mat-icon>\n </a>\n }\n\n <!-- Recursive submenu -->\n @if (item.children && item.children.length > 0 && item.expanded) {\n <ul [class]=\"'submenu level-' + (level + 1)\">\n <ng-container\n *ngTemplateOutlet=\"menuItemsTemplate; context: { $implicit: item.children, level: level + 1 }\"\n ></ng-container>\n </ul>\n }\n </li>\n }\n</ng-template>\n", styles: [".profile-section{text-align:center;cursor:pointer;transition:all .3s ease;display:flex;align-items:center;flex-direction:column;position:relative}.profile-section:hover{transform:scale(.98)}.profile-section .profile-button{display:flex;flex-direction:column;align-items:center;background:none;border:none;padding:0;cursor:pointer;width:100%}.profile-image{width:60px;height:60px;border-radius:50%;object-fit:cover;margin:0 auto 8px;display:block;border:2px solid white;box-shadow:0 2px 4px #0000001a;transition:all .3s ease}.profile-name{font-weight:500;font-size:1rem;color:inherit;transition:opacity .3s ease;margin-top:8px}:host-context(.minimized) .profile-section{padding:10px 0}:host-context(.minimized) .profile-image{width:40px;height:40px;margin:0 auto}.sidenav-footer{position:static;bottom:0;left:0;right:0;padding:16px;color:inherit;font-size:12px;border-top:1px solid rgba(0,0,0,.1);transition:all .3s ease;display:flex;align-items:center;justify-content:space-between;margin-top:auto}.sidenav-footer .version{font-size:10px;margin-top:4px}.footer-button{border:none;background:none;cursor:pointer}:host-context(.minimized) .version{margin-right:7px}:host-context(.minimized) .sidenav-content-wrapper{overflow:hidden}.sidenav-content-wrapper{position:relative;height:100%;display:flex;flex-direction:column;overflow-x:hidden}.sidebar{flex:1;overflow:auto}.sidebar ul{list-style:none;padding:0;margin:0}.sidebar li{margin-bottom:4px;position:relative}.sidebar li a{display:flex;align-items:center;padding:12px 0 0;border-radius:6px;color:var(--mat-sys-primary);text-decoration:none;transition:all .2s ease;position:relative;overflow:hidden}.sidebar li a:hover{background-color:var(--mat-nty-save-record-header-bar);color:var(--mat-nty-save-record-identifier)}.sidebar li a.active{background-color:var(--mat-nty-save-record-header-bar);color:var(--mat-nty-save-record-identifier);font-weight:500}.sidebar li a.active:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:3px;background-color:var(--mat-nty-save-record-header-bar);border-radius:3px 0 0 3px}.sidebar mat-icon{font-size:20px;width:20px;height:20px;color:inherit}.sidebar span{font-size:.875rem;white-space:nowrap}.menu-link.level-2{margin-left:20px;font-size:.82rem}.menu-link.level-3{margin-left:20px;font-size:.8rem}.menu-link.level-4{margin-left:20px;font-size:.78rem}.submenu{padding-left:12px;margin-top:4px}.submenu li a{padding:10px 16px 0 20px}.submenu mat-icon{font-size:18px}.sidebar li a mat-icon:last-child{margin-left:auto;margin-right:0;font-size:18px;color:#5f6368}:host-context(.minimized) .sidebar li a{justify-content:center;padding:12px 0}:host-context(.minimized) .sidenav-content-wrapper button[mat-icon-button]{position:absolute;right:-8px;top:50%;z-index:999}:host-context(.minimized) .sidebar mat-icon{margin-right:0}:host-context(.minimized) .sidebar span,:host-context(.minimized) .sidebar li a mat-icon:last-child{display:none}:host-context(.minimized) .submenu{display:none}.sidenav-content-wrapper button[mat-icon-button]{position:absolute;right:-8px;top:1px;z-index:999}.search-input{margin-top:10px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i3$2.MatNavList, selector: "mat-nav-list", exportAs: ["matNavList"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i5.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: NettyUISearchInput, selector: "ntyui-search-input", outputs: ["search"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
897
|
+
}
|
|
898
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: LeftSidenav, decorators: [{
|
|
899
|
+
type: Component,
|
|
900
|
+
args: [{ selector: 'ntybase-left-sidenav', imports: [
|
|
901
|
+
CommonModule,
|
|
902
|
+
MatIconModule,
|
|
903
|
+
MatListModule,
|
|
904
|
+
MatMenuModule,
|
|
905
|
+
MatMenuTrigger,
|
|
906
|
+
RouterLink,
|
|
907
|
+
RouterModule,
|
|
908
|
+
NettyUISearchInput,
|
|
909
|
+
TranslateModule,
|
|
910
|
+
], template: "<div class=\"sidenav-content-wrapper\">\n <!-- Minimize Icon -->\n <button mat-icon-button (click)=\"onToggleMinimize()\" class=\"minimize-button\">\n <mat-icon class=\"minimize-icon\">\n {{ isMinimized() ? \"chevron_right\" : \"chevron_left\" }}\n </mat-icon>\n </button>\n\n <!-- Profile -->\n <div class=\"profile-section\">\n <button mat-button [matMenuTriggerFor]=\"profileMenu\" class=\"profile-button\">\n <img [src]=\"profileImage()\" [alt]=\"username()\" class=\"profile-image\" />\n @if(!isMinimized()){\n <div class=\"profile-info\">\n <p class=\"profile-name\">{{ username() }}</p>\n </div>\n }\n </button>\n\n <mat-menu #profileMenu=\"matMenu\" xPosition=\"before\">\n <button mat-menu-item disabled>\n <mat-icon>account_circle</mat-icon>\n <span>{{ '@profile' | translate }}</span>\n </button>\n <button mat-menu-item mat-button disabled>\n <mat-icon>swap_horiz</mat-icon>\n <span>{{ '@changeCompany' | translate }}</span>\n </button>\n <button mat-menu-item mat-button (click)=\"logout()\">\n <mat-icon>logout</mat-icon>\n <span>{{ '@logout' | translate }}</span>\n </button>\n </mat-menu>\n </div>\n\n <!-- Search Input -->\n @if (!isMinimized()){\n <ntyui-search-input\n class=\"search-input\"\n [label]=\"'@search' | translate\"\n [placeholder]=\"'@placeholderSearch' | translate\"\n [appearance]=\"'outline'\"\n (search)=\"onSearch($event)\"\n >\n </ntyui-search-input>\n }\n\n <!-- Menu -->\n @if (!isMinimized()) {\n <mat-nav-list>\n <div class=\"sidebar\">\n <ul>\n <!-- Recursive menu render -->\n @let items = filteredMenuItems(); @let level = 0;\n <ng-container\n [ngTemplateOutlet]=\"menuItemsTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: items, level: level }\"\n ></ng-container>\n </ul>\n </div>\n </mat-nav-list>\n }\n\n <!-- Footer -->\n <div class=\"sidenav-footer\">\n <button mat-button [matMenuTriggerFor]=\"footerMenu\" class=\"footer-button\">\n @if (!isMinimized()) {\n <div class=\"footer-text\">\n <div class=\"company-name\">\n <p>© 2025 AXIS</p>\n </div>\n </div>\n }\n </button>\n <div class=\"version\">\n <span class=\"version\">v{{version}}</span>\n </div>\n </div>\n\n <mat-menu #footerMenu=\"matMenu\" xPosition=\"before\">\n <button mat-menu-item disabled>\n <mat-icon>swap_horiz</mat-icon>\n {{ '@changeCompany' | translate }}\n </button>\n <button mat-menu-item mat-button (click)=\"logout()\">\n <mat-icon>logout</mat-icon>\n <span>{{ '@logout' | translate }}</span>\n </button>\n </mat-menu>\n</div>\n\n<!-- Recursive Menu Template -->\n<ng-template #menuItemsTemplate let-items let-level=\"level\">\n @for (item of items; track item.name) {\n <li [class]=\"'menu-level-' + level\">\n <!-- Leaf node (no children) -->\n @if (!item.children || item.children.length === 0) {\n <a\n [routerLink]=\"item.link\"\n routerLinkActive=\"active\"\n [class]=\"'menu-link level-' + level\"\n >\n <mat-icon>{{ item.icon }}</mat-icon>\n <span>{{ item.name }}</span>\n </a>\n }\n\n <!-- Parent node with children -->\n @if (item.children && item.children.length > 0) {\n <a\n (click)=\"toggleSubMenu(item)\"\n [class.expanded]=\"item.expanded\"\n [class]=\"'menu-link has-children level-' + level\"\n >\n <mat-icon>{{ item.icon }}</mat-icon>\n <span>{{ item.name }}</span>\n <mat-icon [class.rotated]=\"item.expanded\" class=\"expand-icon\">\n {{ item.expanded ? 'expand_less' : 'expand_more' }}\n </mat-icon>\n </a>\n }\n\n <!-- Recursive submenu -->\n @if (item.children && item.children.length > 0 && item.expanded) {\n <ul [class]=\"'submenu level-' + (level + 1)\">\n <ng-container\n *ngTemplateOutlet=\"menuItemsTemplate; context: { $implicit: item.children, level: level + 1 }\"\n ></ng-container>\n </ul>\n }\n </li>\n }\n</ng-template>\n", styles: [".profile-section{text-align:center;cursor:pointer;transition:all .3s ease;display:flex;align-items:center;flex-direction:column;position:relative}.profile-section:hover{transform:scale(.98)}.profile-section .profile-button{display:flex;flex-direction:column;align-items:center;background:none;border:none;padding:0;cursor:pointer;width:100%}.profile-image{width:60px;height:60px;border-radius:50%;object-fit:cover;margin:0 auto 8px;display:block;border:2px solid white;box-shadow:0 2px 4px #0000001a;transition:all .3s ease}.profile-name{font-weight:500;font-size:1rem;color:inherit;transition:opacity .3s ease;margin-top:8px}:host-context(.minimized) .profile-section{padding:10px 0}:host-context(.minimized) .profile-image{width:40px;height:40px;margin:0 auto}.sidenav-footer{position:static;bottom:0;left:0;right:0;padding:16px;color:inherit;font-size:12px;border-top:1px solid rgba(0,0,0,.1);transition:all .3s ease;display:flex;align-items:center;justify-content:space-between;margin-top:auto}.sidenav-footer .version{font-size:10px;margin-top:4px}.footer-button{border:none;background:none;cursor:pointer}:host-context(.minimized) .version{margin-right:7px}:host-context(.minimized) .sidenav-content-wrapper{overflow:hidden}.sidenav-content-wrapper{position:relative;height:100%;display:flex;flex-direction:column;overflow-x:hidden}.sidebar{flex:1;overflow:auto}.sidebar ul{list-style:none;padding:0;margin:0}.sidebar li{margin-bottom:4px;position:relative}.sidebar li a{display:flex;align-items:center;padding:12px 0 0;border-radius:6px;color:var(--mat-sys-primary);text-decoration:none;transition:all .2s ease;position:relative;overflow:hidden}.sidebar li a:hover{background-color:var(--mat-nty-save-record-header-bar);color:var(--mat-nty-save-record-identifier)}.sidebar li a.active{background-color:var(--mat-nty-save-record-header-bar);color:var(--mat-nty-save-record-identifier);font-weight:500}.sidebar li a.active:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:3px;background-color:var(--mat-nty-save-record-header-bar);border-radius:3px 0 0 3px}.sidebar mat-icon{font-size:20px;width:20px;height:20px;color:inherit}.sidebar span{font-size:.875rem;white-space:nowrap}.menu-link.level-2{margin-left:20px;font-size:.82rem}.menu-link.level-3{margin-left:20px;font-size:.8rem}.menu-link.level-4{margin-left:20px;font-size:.78rem}.submenu{padding-left:12px;margin-top:4px}.submenu li a{padding:10px 16px 0 20px}.submenu mat-icon{font-size:18px}.sidebar li a mat-icon:last-child{margin-left:auto;margin-right:0;font-size:18px;color:#5f6368}:host-context(.minimized) .sidebar li a{justify-content:center;padding:12px 0}:host-context(.minimized) .sidenav-content-wrapper button[mat-icon-button]{position:absolute;right:-8px;top:50%;z-index:999}:host-context(.minimized) .sidebar mat-icon{margin-right:0}:host-context(.minimized) .sidebar span,:host-context(.minimized) .sidebar li a mat-icon:last-child{display:none}:host-context(.minimized) .submenu{display:none}.sidenav-content-wrapper button[mat-icon-button]{position:absolute;right:-8px;top:1px;z-index:999}.search-input{margin-top:10px}\n"] }]
|
|
911
|
+
}], ctorParameters: () => [], propDecorators: { isMinimized: [{ type: i0.Input, args: [{ isSignal: true, alias: "isMinimized", required: false }] }], toggleMinimize: [{ type: i0.Output, args: ["toggleMinimize"] }] } });
|
|
912
|
+
|
|
913
|
+
class Theme {
|
|
914
|
+
THEME_STORAGE_KEY = 'app-theme';
|
|
915
|
+
appTheme = signal('light', ...(ngDevMode ? [{ debugName: "appTheme" }] : []));
|
|
916
|
+
themes = [
|
|
917
|
+
{ name: 'light', icon: 'light_mode' },
|
|
918
|
+
{ name: 'dark', icon: 'dark_mode' },
|
|
919
|
+
];
|
|
920
|
+
selectedTheme = computed(() => this.themes.find((theme) => theme.name === this.appTheme()), ...(ngDevMode ? [{ debugName: "selectedTheme" }] : []));
|
|
921
|
+
constructor() {
|
|
922
|
+
const savedTheme = this.getSavedTheme();
|
|
923
|
+
if (savedTheme) {
|
|
924
|
+
this.appTheme.set(savedTheme);
|
|
925
|
+
}
|
|
926
|
+
effect(() => {
|
|
927
|
+
const appTheme = this.appTheme();
|
|
928
|
+
document.body.style.setProperty('color-scheme', appTheme);
|
|
929
|
+
document.body.setAttribute('data-ag-theme-mode', appTheme);
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
getThemes() {
|
|
933
|
+
return this.themes;
|
|
934
|
+
}
|
|
935
|
+
setTheme(theme) {
|
|
936
|
+
this.appTheme.set(theme);
|
|
937
|
+
this.saveTheme(theme);
|
|
938
|
+
}
|
|
939
|
+
getSavedTheme() {
|
|
940
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
941
|
+
const saved = localStorage.getItem(this.THEME_STORAGE_KEY);
|
|
942
|
+
return saved === 'light' || saved === 'dark' ? saved : null;
|
|
943
|
+
}
|
|
944
|
+
return null;
|
|
945
|
+
}
|
|
946
|
+
saveTheme(theme) {
|
|
947
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
948
|
+
localStorage.setItem(this.THEME_STORAGE_KEY, theme);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Theme, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
952
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Theme, providedIn: 'root' });
|
|
953
|
+
}
|
|
954
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Theme, decorators: [{
|
|
955
|
+
type: Injectable,
|
|
956
|
+
args: [{
|
|
957
|
+
providedIn: 'root',
|
|
958
|
+
}]
|
|
959
|
+
}], ctorParameters: () => [] });
|
|
960
|
+
|
|
961
|
+
class ColorPalette {
|
|
962
|
+
THEME_STORAGE_KEY = 'selected-theme';
|
|
963
|
+
themes = [];
|
|
964
|
+
currentTheme = signal(null, ...(ngDevMode ? [{ debugName: "currentTheme" }] : []));
|
|
965
|
+
setThemes(customThemes) {
|
|
966
|
+
this.themes = customThemes;
|
|
967
|
+
const savedThemeId = this.getSavedTheme();
|
|
968
|
+
const themeToSet = savedThemeId
|
|
969
|
+
? this.themes.find((t) => t.id === savedThemeId)
|
|
970
|
+
: this.themes[0];
|
|
971
|
+
if (themeToSet) {
|
|
972
|
+
this.currentTheme.set(themeToSet);
|
|
973
|
+
}
|
|
974
|
+
else if (this.themes.length > 0) {
|
|
975
|
+
this.currentTheme.set(this.themes[0]);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
getThemes() {
|
|
979
|
+
return this.themes;
|
|
980
|
+
}
|
|
981
|
+
setTheme(themeId) {
|
|
982
|
+
const theme = this.themes.find((t) => t.id === themeId);
|
|
983
|
+
if (theme) {
|
|
984
|
+
this.currentTheme.set(theme);
|
|
985
|
+
this.saveTheme(themeId);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
getSavedTheme() {
|
|
989
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
990
|
+
return localStorage.getItem(this.THEME_STORAGE_KEY);
|
|
991
|
+
}
|
|
992
|
+
return null;
|
|
993
|
+
}
|
|
994
|
+
saveTheme(themeId) {
|
|
995
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
996
|
+
localStorage.setItem(this.THEME_STORAGE_KEY, themeId);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
updateThemeClass = effect(() => {
|
|
1000
|
+
const theme = this.currentTheme();
|
|
1001
|
+
if (theme) {
|
|
1002
|
+
document.body.classList.remove(...this.themes.map((t) => `${t.id}-theme`));
|
|
1003
|
+
document.body.classList.add(`${theme.id}-theme`);
|
|
1004
|
+
}
|
|
1005
|
+
}, ...(ngDevMode ? [{ debugName: "updateThemeClass" }] : []));
|
|
1006
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ColorPalette, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1007
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ColorPalette, providedIn: 'root' });
|
|
1008
|
+
}
|
|
1009
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ColorPalette, decorators: [{
|
|
1010
|
+
type: Injectable,
|
|
1011
|
+
args: [{
|
|
1012
|
+
providedIn: 'root',
|
|
1013
|
+
}]
|
|
1014
|
+
}] });
|
|
1015
|
+
|
|
1016
|
+
class Toolbar {
|
|
1017
|
+
themeService = inject(Theme);
|
|
1018
|
+
colorPaletteService = inject(ColorPalette);
|
|
1019
|
+
i18nService = inject(I18nService);
|
|
1020
|
+
router = inject(Router);
|
|
1021
|
+
toggleSidenav = output();
|
|
1022
|
+
onToggleSidenav() {
|
|
1023
|
+
this.toggleSidenav.emit();
|
|
1024
|
+
}
|
|
1025
|
+
// Language
|
|
1026
|
+
icon = model(false, ...(ngDevMode ? [{ debugName: "icon" }] : []));
|
|
1027
|
+
get currentLanguage() {
|
|
1028
|
+
return this.i18nService.language;
|
|
1029
|
+
}
|
|
1030
|
+
get languages() {
|
|
1031
|
+
return this.i18nService.supportedLanguages;
|
|
1032
|
+
}
|
|
1033
|
+
setLanguage(language) {
|
|
1034
|
+
this.i18nService.language = language;
|
|
1035
|
+
setTimeout(() => {
|
|
1036
|
+
window.location.reload();
|
|
1037
|
+
}, 100);
|
|
1038
|
+
}
|
|
1039
|
+
getCurrentLanguageIcon() {
|
|
1040
|
+
switch (this.i18nService.language) {
|
|
1041
|
+
case 'Türkçe':
|
|
1042
|
+
return 'fi fi-tr';
|
|
1043
|
+
case 'English':
|
|
1044
|
+
return 'fi fi-us';
|
|
1045
|
+
default:
|
|
1046
|
+
return 'fi fi-tr';
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
getLanguageIcon(language) {
|
|
1050
|
+
switch (language) {
|
|
1051
|
+
case 'Türkçe':
|
|
1052
|
+
return 'fi fi-tr';
|
|
1053
|
+
case 'English':
|
|
1054
|
+
return 'fi fi-us';
|
|
1055
|
+
default:
|
|
1056
|
+
return 'fi fi-tr';
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Toolbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1060
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: Toolbar, isStandalone: true, selector: "ntybase-toolbar", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleSidenav: "toggleSidenav", icon: "iconChange" }, ngImport: i0, template: "<mat-toolbar class=\"sidenav-header\">\n <div class=\"left-section\">\n <button mat-icon-button (click)=\"onToggleSidenav()\" class=\"menu-button\">\n <mat-icon>menu</mat-icon>\n </button>\n <span>{{'app_name' | translate }}</span>\n\n <!-- Language Selection -->\n <div class=\"language-toggle\">\n @if (icon()) {\n <button mat-icon-button [matMenuTriggerFor]=\"languageMenu\">\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n } @else {\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n }\n </div>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n @for (language of languages; track language) {\n <button mat-menu-item (click)=\"setLanguage(language)\">\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n }\n </mat-menu>\n </div>\n\n <div class=\"spacer\"></div>\n\n <div class=\"flex-stretch\"></div>\n <button\n mat-icon-button\n [mat-menu-trigger-for]=\"themeMenu\"\n class=\"theme-button\"\n >\n <mat-icon>{{ themeService.selectedTheme()?.icon }}</mat-icon>\n </button>\n <mat-menu #themeMenu=\"matMenu\">\n @for (theme of themeService.getThemes(); track theme.name) {\n <button\n [class.selected-theme]=\"themeService.selectedTheme()?.name === theme.name\"\n mat-menu-item\n (click)=\"themeService.setTheme(theme.name)\"\n >\n <mat-icon>{{ theme.icon }}</mat-icon>\n <span>{{ theme.name | titlecase }}</span>\n </button>\n }\n </mat-menu>\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"customThemeMenu\"\n class=\"custom-theme-button\"\n >\n <mat-icon>format_color_fill</mat-icon>\n </button>\n <mat-menu #customThemeMenu=\"matMenu\">\n @for (customTheme of colorPaletteService.getThemes(); track customTheme.id)\n {\n <button\n mat-menu-item\n (click)=\"colorPaletteService.setTheme(customTheme.id)\"\n >\n <div class=\"theme-menu-item\">\n <div\n class=\"color-preview\"\n [style.background-color]=\"customTheme.primary\"\n ></div>\n <span>{{ customTheme.displayName }}</span>\n </div>\n </button>\n }\n </mat-menu>\n</mat-toolbar>\n", styles: ["mat-toolbar{position:fixed;top:0;left:0;right:0;z-index:3;box-shadow:0 1px 5px #0000001a;display:flex;align-items:center}.sidenav-header{display:flex;justify-content:space-between;align-items:center;padding:16px;margin:0;font-weight:500}.sidenav-header .left-section{display:flex;align-items:center;gap:16px}.sidenav-header .spacer{flex:1 1 auto}.flex-stretch{flex:1 0 auto}.theme-menu-item{display:flex;align-items:center;gap:12px}.color-preview{width:24px;height:24px;border-radius:50%}::ng-deep .theme-button{border:none;background:none;margin-right:15px;cursor:pointer}::ng-deep .custom-theme-button{border:none;background:none;cursor:pointer}::ng-deep .menu-button{border:none;background:none;cursor:pointer}.language-button{margin:0 auto}::ng-deep button{border:none;background:none;cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i3$3.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1.TitleCasePipe, name: "titlecase" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
1061
|
+
}
|
|
1062
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Toolbar, decorators: [{
|
|
1063
|
+
type: Component,
|
|
1064
|
+
args: [{ selector: 'ntybase-toolbar', imports: [
|
|
1065
|
+
MatIconModule,
|
|
1066
|
+
MatMenuModule,
|
|
1067
|
+
MatToolbarModule,
|
|
1068
|
+
CommonModule,
|
|
1069
|
+
TitleCasePipe,
|
|
1070
|
+
TranslateModule,
|
|
1071
|
+
], template: "<mat-toolbar class=\"sidenav-header\">\n <div class=\"left-section\">\n <button mat-icon-button (click)=\"onToggleSidenav()\" class=\"menu-button\">\n <mat-icon>menu</mat-icon>\n </button>\n <span>{{'app_name' | translate }}</span>\n\n <!-- Language Selection -->\n <div class=\"language-toggle\">\n @if (icon()) {\n <button mat-icon-button [matMenuTriggerFor]=\"languageMenu\">\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n } @else {\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n }\n </div>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n @for (language of languages; track language) {\n <button mat-menu-item (click)=\"setLanguage(language)\">\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n }\n </mat-menu>\n </div>\n\n <div class=\"spacer\"></div>\n\n <div class=\"flex-stretch\"></div>\n <button\n mat-icon-button\n [mat-menu-trigger-for]=\"themeMenu\"\n class=\"theme-button\"\n >\n <mat-icon>{{ themeService.selectedTheme()?.icon }}</mat-icon>\n </button>\n <mat-menu #themeMenu=\"matMenu\">\n @for (theme of themeService.getThemes(); track theme.name) {\n <button\n [class.selected-theme]=\"themeService.selectedTheme()?.name === theme.name\"\n mat-menu-item\n (click)=\"themeService.setTheme(theme.name)\"\n >\n <mat-icon>{{ theme.icon }}</mat-icon>\n <span>{{ theme.name | titlecase }}</span>\n </button>\n }\n </mat-menu>\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"customThemeMenu\"\n class=\"custom-theme-button\"\n >\n <mat-icon>format_color_fill</mat-icon>\n </button>\n <mat-menu #customThemeMenu=\"matMenu\">\n @for (customTheme of colorPaletteService.getThemes(); track customTheme.id)\n {\n <button\n mat-menu-item\n (click)=\"colorPaletteService.setTheme(customTheme.id)\"\n >\n <div class=\"theme-menu-item\">\n <div\n class=\"color-preview\"\n [style.background-color]=\"customTheme.primary\"\n ></div>\n <span>{{ customTheme.displayName }}</span>\n </div>\n </button>\n }\n </mat-menu>\n</mat-toolbar>\n", styles: ["mat-toolbar{position:fixed;top:0;left:0;right:0;z-index:3;box-shadow:0 1px 5px #0000001a;display:flex;align-items:center}.sidenav-header{display:flex;justify-content:space-between;align-items:center;padding:16px;margin:0;font-weight:500}.sidenav-header .left-section{display:flex;align-items:center;gap:16px}.sidenav-header .spacer{flex:1 1 auto}.flex-stretch{flex:1 0 auto}.theme-menu-item{display:flex;align-items:center;gap:12px}.color-preview{width:24px;height:24px;border-radius:50%}::ng-deep .theme-button{border:none;background:none;margin-right:15px;cursor:pointer}::ng-deep .custom-theme-button{border:none;background:none;cursor:pointer}::ng-deep .menu-button{border:none;background:none;cursor:pointer}.language-button{margin:0 auto}::ng-deep button{border:none;background:none;cursor:pointer}\n"] }]
|
|
1072
|
+
}], propDecorators: { toggleSidenav: [{ type: i0.Output, args: ["toggleSidenav"] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }, { type: i0.Output, args: ["iconChange"] }] } });
|
|
1073
|
+
|
|
1074
|
+
/*
|
|
1075
|
+
* Public API Surface of ntyux
|
|
1076
|
+
*/
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Generated bundle index. Do not edit.
|
|
1080
|
+
*/
|
|
1081
|
+
|
|
1082
|
+
export { AutoComplete, ColorPalette, HttpError403, HttpError404, LeftSidenav, Ntyux, NtyuxModule, SelectionItem, Theme, Toolbar };
|
|
1083
|
+
//# sourceMappingURL=nettyapps-ntyux.mjs.map
|