@yomotech/view-toolkit 0.0.30 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/ng-package.json +7 -0
  2. package/package.json +38 -49
  3. package/src/lib/app-version.ts +2 -0
  4. package/src/lib/components/autocomplete/autocomplete.css +0 -0
  5. package/src/lib/components/autocomplete/autocomplete.html +23 -0
  6. package/src/lib/components/autocomplete/autocomplete.ts +71 -0
  7. package/src/lib/components/button/button.css +0 -0
  8. package/src/lib/components/button/button.html +1 -0
  9. package/src/lib/components/button/button.ts +11 -0
  10. package/src/lib/components/date/date.css +0 -0
  11. package/src/lib/components/date/date.html +1 -0
  12. package/src/lib/components/date/date.ts +11 -0
  13. package/src/lib/components/dropdown/dropdown.css +0 -0
  14. package/src/lib/components/dropdown/dropdown.html +1 -0
  15. package/src/lib/components/dropdown/dropdown.ts +11 -0
  16. package/src/lib/components/error-message/error-message.css +0 -0
  17. package/src/lib/components/error-message/error-message.html +1 -0
  18. package/src/lib/components/error-message/error-message.ts +11 -0
  19. package/src/lib/components/label/label.css +0 -0
  20. package/src/lib/components/label/label.html +7 -0
  21. package/src/lib/components/label/label.ts +33 -0
  22. package/src/lib/components/string/string.css +0 -0
  23. package/src/lib/components/string/string.html +4 -0
  24. package/src/lib/components/string/string.ts +22 -0
  25. package/src/lib/components/textbox/textbox.css +0 -0
  26. package/src/lib/components/textbox/textbox.html +7 -0
  27. package/src/lib/components/textbox/textbox.ts +33 -0
  28. package/src/lib/components/time/time.css +0 -0
  29. package/src/lib/components/time/time.html +1 -0
  30. package/src/lib/components/time/time.ts +11 -0
  31. package/src/lib/containers/card/card.component.css +0 -0
  32. package/src/lib/containers/card/card.component.html +450 -0
  33. package/src/lib/containers/card/card.component.ts +474 -0
  34. package/src/lib/containers/form/form.component.css +101 -0
  35. package/src/lib/containers/form/form.component.html +305 -0
  36. package/src/lib/containers/form/form.component.ts +333 -0
  37. package/src/lib/containers/grid/grid.component.css +0 -0
  38. package/src/lib/containers/grid/grid.component.html +212 -0
  39. package/src/lib/containers/grid/grid.component.ts +228 -0
  40. package/src/lib/containers/header-menu/header-menu.component.css +0 -0
  41. package/src/lib/containers/header-menu/header-menu.component.html +72 -0
  42. package/src/lib/containers/header-menu/header-menu.component.ts +105 -0
  43. package/src/lib/containers/logs/logs.component.css +160 -0
  44. package/src/lib/containers/logs/logs.component.html +89 -0
  45. package/src/lib/containers/logs/logs.component.ts +29 -0
  46. package/src/lib/containers/model/model.component.css +0 -0
  47. package/src/lib/containers/model/model.component.html +351 -0
  48. package/src/lib/containers/model/model.component.ts +436 -0
  49. package/src/lib/containers/pagination/pagination.component.css +0 -0
  50. package/src/lib/containers/pagination/pagination.component.html +41 -0
  51. package/src/lib/containers/pagination/pagination.component.ts +100 -0
  52. package/src/lib/containers/side-menu/side-menu.component.css +173 -0
  53. package/src/lib/containers/side-menu/side-menu.component.html +126 -0
  54. package/src/lib/containers/side-menu/side-menu.component.ts +458 -0
  55. package/src/lib/services/app-version-reset.service.ts +59 -0
  56. package/src/lib/services/config.service.ts +144 -0
  57. package/src/lib/services/couchdb.ts +275 -0
  58. package/src/lib/services/mobile-keycloak.service.ts +116 -0
  59. package/src/lib/services/network-log.service.ts +27 -0
  60. package/src/lib/services/notification.service.ts +26 -0
  61. package/src/lib/services/request.service.ts +125 -0
  62. package/src/lib/services/utility.service.ts +600 -0
  63. package/src/lib/view-toolkit.module.ts +57 -0
  64. package/src/lib/view-toolkit.spec.ts +23 -0
  65. package/src/lib/view-toolkit.ts +15 -0
  66. package/src/public-api.ts +25 -0
  67. package/tsconfig.lib.json +17 -0
  68. package/tsconfig.lib.prod.json +11 -0
  69. package/tsconfig.spec.json +15 -0
  70. package/fesm2022/yomotech-view-toolkit.mjs +0 -3538
  71. package/fesm2022/yomotech-view-toolkit.mjs.map +0 -1
  72. package/types/yomotech-view-toolkit.d.ts +0 -644
@@ -0,0 +1,436 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { AfterViewInit, Component, EventEmitter, Input, Output, SimpleChanges } from '@angular/core';
3
+ import { FormsModule, NgForm } from '@angular/forms';
4
+ import { CapacitorHttp } from '@capacitor/core';
5
+ import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
6
+ import { NzModalModule } from 'ng-zorro-antd/modal';
7
+ import { NzSelectModule } from 'ng-zorro-antd/select';
8
+ import { AutoCompleteModule } from 'primeng/autocomplete';
9
+ import { DatePickerModule } from 'primeng/datepicker';
10
+ import { InputTextModule } from 'primeng/inputtext';
11
+ import { Label } from '../../components/label/label';
12
+ import { Textbox } from '../../components/textbox/textbox';
13
+ import { String } from '../../components/string/string';
14
+ import { Autocomplete } from '../../components/autocomplete/autocomplete';
15
+
16
+ @Component({
17
+ standalone: true,
18
+ selector: 'vl-model',
19
+ templateUrl: './model.component.html',
20
+ styleUrl: './model.component.css',
21
+ imports: [
22
+ CommonModule,
23
+ FormsModule,
24
+ NzModalModule,
25
+ NzSelectModule,
26
+ NzDatePickerModule,
27
+ DatePickerModule,
28
+ AutoCompleteModule,
29
+ InputTextModule,
30
+ Label,
31
+ Textbox,
32
+ String,
33
+ Autocomplete
34
+ ]
35
+ })
36
+ export class ModelComponent implements AfterViewInit {
37
+ matchData: any[] = [];
38
+ @Input() errorMessages: any = {};
39
+ @Input() isModalShow: boolean = false;
40
+ @Input() modalItemInfo: any = {};
41
+ @Input() selectedLanguageInfo: any = {};
42
+ @Input() userInfo: any = {};
43
+ @Output() invokeButtonClick: EventEmitter<any> = new EventEmitter();
44
+ @Output() invokeSetFormData: EventEmitter<any> = new EventEmitter();
45
+ @Output() modalClose = new EventEmitter<void>();
46
+ copyLayoutSchema: any = {};
47
+ isLoading: boolean = false;
48
+
49
+ constructor() {
50
+ }
51
+
52
+ ngAfterViewInit(): void {
53
+ }
54
+
55
+ async ngOnChanges(changes: SimpleChanges) {
56
+ if (this.isModalShow === true) {
57
+ if (this.modalItemInfo.layoutSchema) {
58
+ this.copyLayoutSchema = JSON.parse(JSON.stringify(this.modalItemInfo.layoutSchema));
59
+ }
60
+ if ((changes['selectedLanguageInfo'] || this.selectedLanguageInfo) && this.selectedLanguageInfo.selectedLanguage) {
61
+ this.modalItemInfo.layoutSchema = this.updateLayoutLanguage(this.copyLayoutSchema, this.selectedLanguageInfo.selectedLanguage)
62
+ }
63
+ if (this.modalItemInfo?.currentRecord) {
64
+ this.mapDataToPropertySchema(
65
+ this.modalItemInfo.currentRecord,
66
+ this.modalItemInfo
67
+ );
68
+ }
69
+ await this.setAutoCompleteData();
70
+ }
71
+ }
72
+
73
+ updateLayoutLanguage<T>(
74
+ schema: T,
75
+ translations: Record<string, string>
76
+ ): T {
77
+ const cloned = JSON.parse(JSON.stringify(schema));
78
+
79
+ const walk = (obj: any) => {
80
+ if (!obj || typeof obj !== 'object') return;
81
+
82
+ // 🔹 label.caption (most common in form schemas)
83
+ if (obj.label && typeof obj.label.caption === 'string') {
84
+ const original = obj.label.caption;
85
+ obj.label.caption = translations[original] ?? original;
86
+ }
87
+
88
+ // 🔹 direct caption (fallback / buttons / future-proof)
89
+ if (typeof obj.caption === 'string') {
90
+ const original = obj.caption;
91
+ obj.caption = translations[original] ?? original;
92
+ }
93
+
94
+ // 🚫 Skip huge data arrays (autocomplete datasets, etc.)
95
+ if (Array.isArray(obj.data)) return;
96
+
97
+ // 🔁 Recurse
98
+ Object.keys(obj).forEach((key) => {
99
+ const value = obj[key];
100
+ if (typeof value === 'object') {
101
+ walk(value);
102
+ }
103
+ });
104
+ };
105
+
106
+ walk(cloned);
107
+ return cloned;
108
+ }
109
+
110
+
111
+ mapDataToPropertySchema(data: any, modalItemInfo: any): void {
112
+ if (!data || !modalItemInfo?.propertySchema) return;
113
+ const [rootScope] = Object.keys(modalItemInfo.propertySchema);
114
+ if (!rootScope) return;
115
+
116
+ const schemaFields = modalItemInfo.propertySchema[rootScope];
117
+ Object.keys(schemaFields).forEach((fieldKey) => {
118
+ if (data.hasOwnProperty(fieldKey)) {
119
+ if (!schemaFields[fieldKey]) {
120
+ schemaFields[fieldKey] = {};
121
+ }
122
+ schemaFields[fieldKey].value = data[fieldKey];
123
+ }
124
+ });
125
+ }
126
+
127
+
128
+ async setAutoCompleteData() {
129
+ for (const section of this.modalItemInfo.layoutSchema.formSections) {
130
+ for (const field of section.formFields || []) {
131
+ const type =
132
+ this.modalItemInfo.propertySchema[
133
+ this.getScope(field.scope?.reference)
134
+ ]?.[field.scope.reference.split('/').pop()]?.type;
135
+ if (type === 'autocomplete' && field.option?.autocomplete) {
136
+ this.isLoading = true;
137
+ field['data'] = await this.fetchAutocompleteData(field);
138
+ this.isLoading = false;
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ async fetchAutocompleteData(formField: any) {
145
+ const config = formField.option?.autocomplete;
146
+ if (!config) return;
147
+ const request: any = this.resolveAutocompleteConfig(config, this.userInfo);
148
+ const response = await CapacitorHttp.request({
149
+ url: request.url,
150
+ method: request.method,
151
+ params: request.params,
152
+ headers: request.headers
153
+ });
154
+
155
+ return response.data?.length > 0 ? response.data : [];
156
+ }
157
+
158
+ resolveAutocompleteConfig(config: any, userInfo: any) {
159
+ const params: any = {};
160
+ Object.entries(config.params || {}).forEach(([key, value]: any) => {
161
+ // 🔹 Dynamic token
162
+ if (typeof value === 'string' && value.includes('{{')) {
163
+ params[key] = this.resolveToken(value, userInfo);
164
+ }
165
+ // 🔹 Computed param
166
+ else if (value?.type === 'company') {
167
+ params[key] = this.buildCompany(userInfo);
168
+ }
169
+ // 🔹 Static
170
+ else {
171
+ params[key] = value;
172
+ }
173
+ });
174
+
175
+ return {
176
+ url: `${userInfo.p5_backend_url}${config.url}`,
177
+ method: config.method || 'POST',
178
+ params,
179
+ headers: config.passToken
180
+ ? {
181
+ Authorization: `Bearer ${userInfo.token}`,
182
+ Accept: 'application/json'
183
+ }
184
+ : {}
185
+ };
186
+ }
187
+
188
+ buildCompany(userInfo: any): string {
189
+ return userInfo.p5_plant
190
+ ? `${userInfo.p5_group}/${userInfo.p5_client}/${userInfo.p5_plant}`
191
+ : `${userInfo.p5_group}/${userInfo.p5_client}/`;
192
+ }
193
+
194
+ resolveToken(template: string, context: any) {
195
+ return template.replace(/{{(.*?)}}/g, (_, path) => {
196
+ return path.split('.').reduce((acc: any, key: any) => acc?.[key], context) ?? '';
197
+ });
198
+ }
199
+
200
+ getScope(scope: any) {
201
+ let propertyScope = "";
202
+ let start = scope?.indexOf("#/") + 2;
203
+ let end = scope?.indexOf("/", start);
204
+ if (start && end) {
205
+ propertyScope = scope.substring(start, end);
206
+ }
207
+ return propertyScope;
208
+ }
209
+
210
+ autocompChangeEvent(event: any) {
211
+ const q = (event?.event.query || '').toLowerCase();
212
+ let fieldName = event?.formField.scope?.reference?.split('/')?.pop()
213
+ if (fieldName) {
214
+ this.matchData = event?.formField.data?.filter((x: any) => x[fieldName] && x[fieldName]?.toLowerCase()?.startsWith(q));
215
+ }
216
+ }
217
+
218
+ autocompFocusOutEvent(event: any) {
219
+ const selected = event?.event.target.value;
220
+ let fieldName = event?.formField.scope?.reference?.split('/')?.pop();
221
+ if (fieldName && (selected || typeof selected !== 'object')) {
222
+ let actualObj = event?.formField.data?.find((x: any) => x[fieldName] == selected)
223
+ if (actualObj) {
224
+ this.autocompSelectEvent(event)
225
+ }
226
+ }
227
+ }
228
+
229
+ autocompSelectEvent(event: any) {
230
+ const selected = event?.event.value;
231
+ const mapping = event.formField.option?.onSelect?.mapFields;
232
+ if (!selected || !mapping) return;
233
+ Object.entries(mapping).forEach(([targetProp, sourceKey]) => {
234
+ const scope = this.getScope(event.formField.scope.reference);
235
+ if (this.modalItemInfo.propertySchema?.[scope]?.[targetProp]) {
236
+ this.modalItemInfo.propertySchema[scope][targetProp].value = selected[sourceKey as string] ?? null;
237
+ }
238
+ });
239
+ }
240
+
241
+ setErroMessage(formField: any, errorMessages: any, errorType: any) {
242
+ let error: any = "";
243
+ let fieldName: any = formField.label?.value ? formField.label?.value : formField.label?.caption ? formField.label?.caption : this.modalItemInfo.propertySchema[this.getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption;
244
+ if (errorType == "required") {
245
+ error = this.selectedLanguageInfo?.errorMessages?.required ? this.selectedLanguageInfo?.errorMessages.required : errorMessages.required
246
+ }
247
+ if (error && error.toString().includes("{{requiredLength}}")) {
248
+ error = error.replace(/{{\s*requiredLength\s*}}/g, formField?.minlength.toString());
249
+ }
250
+ return error.replace(/{{\s*field\s*}}/g, fieldName);
251
+ }
252
+
253
+ getStyleWithoutClasses(styleObject: any) {
254
+ if (!styleObject) {
255
+ return { classes: '', style: {} };
256
+ }
257
+ const { classes, ...styleWithoutClasses } = styleObject;
258
+ return { classes: classes && classes.length > 0 ? classes[0] : '', style: styleWithoutClasses };
259
+ }
260
+
261
+ dateChangeEvent(formField: any, value: any) {
262
+ this.handleRules(formField, value);
263
+ }
264
+
265
+ onDateTyping(event: any) {
266
+ const input = event.target as HTMLInputElement;
267
+ if (!input) return;
268
+ let digits = input.value.replace(/\D/g, '');
269
+ digits = digits.substring(0, 8);
270
+ let formatted = '';
271
+ if (digits.length <= 4) {
272
+ formatted = digits;
273
+ } else if (digits.length <= 6) {
274
+ formatted = `${digits.slice(0, 4)}-${digits.slice(4)}`;
275
+ } else {
276
+ formatted = `${digits.slice(0, 4)}-${digits.slice(4, 6)}-${digits.slice(6)}`;
277
+ }
278
+ input.value = formatted;
279
+ }
280
+
281
+ forceNumericKeyboard(event: any) {
282
+ const input = event?.target as HTMLInputElement;
283
+ if (input && input.tagName === 'INPUT') {
284
+ input.setAttribute('inputmode', 'numeric'); // Android
285
+ input.setAttribute('pattern', '[0-9:]*'); // iOS
286
+ input.setAttribute('type', 'tel'); // strongest signal
287
+ }
288
+ }
289
+
290
+ onTimeTyping(event: any) {
291
+ const input: HTMLInputElement =
292
+ event?.target || event?.originalEvent?.target;
293
+ if (!input) return;
294
+ let digits = input.value.replace(/\D/g, '');
295
+ digits = digits.substring(0, 4);
296
+ let formatted = '';
297
+ if (digits.length <= 2) {
298
+ formatted = digits;
299
+ } else {
300
+ formatted = `${digits.slice(0, 2)}:${digits.slice(2)}`;
301
+ }
302
+
303
+ input.value = formatted;
304
+ }
305
+
306
+ timeChangeEvent(formField: any, value: any) {
307
+ this.handleRules(formField, value);
308
+ }
309
+
310
+ handleRules(formField: any, value: any) {
311
+ const scope = this.getScope(formField.scope.reference);
312
+ const field = formField.scope?.reference.split('/').pop()
313
+ const rules = formField.option?.rules;
314
+ this.modalItemInfo.propertySchema[scope][field].value = value;
315
+ if (!rules) return;
316
+ rules.forEach((rule: any) => {
317
+ let exist = this.isRuleTriggered(rule, formField)
318
+ if (!exist) return;
319
+ switch (rule.action) {
320
+ case 'calculateDuration':
321
+ this.applyDurationRule(rule, formField);
322
+ break;
323
+ case 'resetValue':
324
+ this.resetRule(rule, formField);
325
+ break;
326
+ }
327
+ });
328
+ }
329
+
330
+ resetRule(rule: any, formField: any) {
331
+ const scope = this.getScope(formField.scope.reference);
332
+ if (rule?.resetValue?.length > 0) {
333
+ rule.resetValue.forEach((prop: any) => {
334
+ this.modalItemInfo.propertySchema[scope][prop].value = null;
335
+ });
336
+ }
337
+ }
338
+
339
+ isRuleTriggered(rule: any, formField: any): boolean {
340
+ if (rule.when === 'timeRangeComplete') {
341
+ const scope = this.getScope(formField.scope.reference);
342
+ return (
343
+ this.modalItemInfo.propertySchema[scope]?.workDayTimeFrom?.value &&
344
+ this.modalItemInfo.propertySchema[scope]?.workDayTimeTo?.value
345
+ );
346
+ } else if (rule.when === 'change') {
347
+ return true;
348
+ }
349
+ return false;
350
+ }
351
+
352
+ applyDurationRule(rule: any, formField: any) {
353
+ const scope = this.getScope(formField.scope.reference);
354
+ const schema = this.modalItemInfo.propertySchema[scope];
355
+ const fromTimeVal: Date = schema?.workDayTimeFrom?.value;
356
+ const toTimeVal: Date = schema?.workDayTimeTo?.value;
357
+ const baseDateVal: Date = schema?.fromDate?.value;
358
+
359
+ // 🔒 Guard
360
+ if (!fromTimeVal || !toTimeVal || !baseDateVal) {
361
+ schema[rule.params.durationField].value = '';
362
+ return;
363
+ }
364
+
365
+ // 📅 Base date (From Date)
366
+ const baseDate = new Date(baseDateVal);
367
+ baseDate.setHours(0, 0, 0, 0);
368
+
369
+ // ⏱ FROM datetime
370
+ const fromDateTime = new Date(baseDate);
371
+ fromDateTime.setHours(
372
+ fromTimeVal.getHours(),
373
+ fromTimeVal.getMinutes(),
374
+ 0,
375
+ 0
376
+ );
377
+
378
+ // ⏱ TO datetime
379
+ let toDateTime = new Date(baseDate);
380
+ toDateTime.setHours(
381
+ toTimeVal.getHours(),
382
+ toTimeVal.getMinutes(),
383
+ 0,
384
+ 0
385
+ );
386
+
387
+ // 🌙 Overnight handling
388
+ if (toDateTime <= fromDateTime) {
389
+ toDateTime.setDate(toDateTime.getDate() + 1);
390
+ }
391
+
392
+ // ⏳ Duration calculation
393
+ const diffMs = toDateTime.getTime() - fromDateTime.getTime();
394
+ const totalMinutes = Math.floor(diffMs / 60000);
395
+ const hours = Math.floor(totalMinutes / 60);
396
+ const minutes = totalMinutes % 60;
397
+ const duration = `${hours}:${minutes.toString().padStart(2, '0')}`;
398
+ // ✅ Set duration
399
+ schema[rule.params.durationField].value = duration;
400
+
401
+ // ✅ Auto set To Date
402
+ schema.toDate.value = new Date(toDateTime);
403
+ }
404
+
405
+ invokeSetformData() {
406
+ this.invokeSetFormData.emit();
407
+ }
408
+
409
+ invokeFormBtnClick(option: any, propertySchema: any, form: NgForm) {
410
+ if (option.innerAction === 'submit') {
411
+ let valid = this.checkValidty(form);
412
+ if (valid) {
413
+ this.invokeButtonClick.emit({ option: option, propertySchema: propertySchema, recordIndex: this.modalItemInfo?.recordIndex });
414
+ }
415
+ } else {
416
+ this.invokeButtonClick.emit({ option: option, propertySchema: propertySchema });
417
+ }
418
+ }
419
+
420
+ checkValidty(form: NgForm): boolean {
421
+ if (!form) {
422
+ return false;
423
+ }
424
+
425
+ Object.values(form.controls).forEach(control => {
426
+ control.markAsTouched();
427
+ control.updateValueAndValidity();
428
+ });
429
+
430
+ return !!form.valid;
431
+ }
432
+
433
+ onCancel() {
434
+ this.modalClose.emit();
435
+ }
436
+ }
@@ -0,0 +1,41 @@
1
+ <div [ngStyle]="paginationStyle">
2
+ <ul [ngStyle]="listStyle" [style.z-index]="isMobile ? 99999 : 'auto'">
3
+
4
+ <li (click)="invokeGoToPage(0)" [ngStyle]="getPageItemStyle(undefined, currentPage === 0)">
5
+ ««
6
+ </li>
7
+
8
+ <li (click)="invokeGoToPage(currentPage - 1)" [ngStyle]="getPageItemStyle(undefined, currentPage === 0)">
9
+ «
10
+ </li>
11
+
12
+ <li *ngFor="let page of pages" (click)="invokeGoToPage(page)" [ngStyle]="getPageItemStyle(page)">
13
+ {{ page + 1 }}
14
+ </li>
15
+
16
+ <li (click)="invokeGoToPage(currentPage + 1)"
17
+ [ngStyle]="getPageItemStyle(undefined, currentPage === pages.length - 1)">
18
+ »
19
+ </li>
20
+
21
+ <li (click)="invokeGoToPage(pages.length - 1)"
22
+ [ngStyle]="getPageItemStyle(undefined, currentPage === pages.length - 1)">
23
+ »»
24
+ </li>
25
+ </ul>
26
+
27
+ <div *ngIf="itemInfo?.layoutSchema?.pagination?.pageSize" [ngStyle]="sizeControlStyle">
28
+
29
+ <label for="pageSize" [ngStyle]="labelStyle">
30
+ {{ itemInfo?.layoutSchema?.pagination?.caption }}
31
+ </label>
32
+
33
+ <select id="pageSize" [(ngModel)]="pageSize" (change)="invokeOnPageSizeChange(pageSize)"
34
+ [ngStyle]="selectStyle">
35
+
36
+ <option *ngFor="let size of [5, 10, 15, 20]" [value]="size">
37
+ {{ size }}
38
+ </option>
39
+ </select>
40
+ </div>
41
+ </div>
@@ -0,0 +1,100 @@
1
+ import { Platform, PlatformModule } from '@angular/cdk/platform';
2
+ import { CommonModule } from '@angular/common';
3
+ import { Component, EventEmitter, Input, Output } from '@angular/core';
4
+ import { FormsModule } from '@angular/forms';
5
+
6
+ @Component({
7
+ standalone: true,
8
+ selector: 'vl-pagination',
9
+ templateUrl: './pagination.component.html',
10
+ styleUrl: './pagination.component.css',
11
+ imports: [
12
+ CommonModule,
13
+ FormsModule,
14
+ PlatformModule
15
+ ]
16
+ })
17
+ export class PaginationComponent {
18
+ @Input() itemInfo: any = {
19
+ layoutSchema: {},
20
+ propertySchema: {},
21
+ data: []
22
+ };
23
+
24
+ @Input() pageSize: any;
25
+ @Input() currentPage: number = 0;
26
+ @Input() pages: number[] = [];
27
+
28
+ @Output() pageSizeChange: EventEmitter<any> = new EventEmitter();
29
+ @Output() onGoToPage: EventEmitter<any> = new EventEmitter();
30
+
31
+ isMobile: boolean = false;
32
+
33
+ constructor(private platform: Platform) { }
34
+
35
+ ngOnInit() {
36
+ if (this.platform.ANDROID || this.platform.IOS) {
37
+ this.isMobile = true;
38
+ }
39
+ }
40
+
41
+ get paginationStyle(): any {
42
+ return this.getStyle(this.itemInfo?.layoutSchema?.pagination?.style);
43
+ }
44
+
45
+ get listStyle(): any {
46
+ return this.getStyle(this.itemInfo?.layoutSchema?.pagination?.listStyle);
47
+ }
48
+
49
+ get sizeControlStyle(): any {
50
+ return this.getStyle(this.itemInfo?.layoutSchema?.pagination?.sizeControlStyle);
51
+ }
52
+
53
+ get labelStyle(): any {
54
+ return this.getStyle(this.itemInfo?.layoutSchema?.pagination?.labelStyle);
55
+ }
56
+
57
+ get selectStyle(): any {
58
+ return this.getStyle(this.itemInfo?.layoutSchema?.pagination?.selectStyle);
59
+ }
60
+
61
+ getStyle(styleObject: any): Record<string, any> {
62
+ if (!styleObject || typeof styleObject !== 'object') {
63
+ return {};
64
+ }
65
+
66
+ const { classes, ...styleWithoutClasses } = styleObject;
67
+ return styleWithoutClasses;
68
+ }
69
+
70
+ getPageItemStyle(page?: number, disabled: boolean = false): Record<string, any> {
71
+ const pagination = this.itemInfo?.layoutSchema?.pagination || {};
72
+
73
+ const baseStyle = this.getStyle(pagination.itemStyle);
74
+ const activeStyle = page !== undefined && this.currentPage === page
75
+ ? this.getStyle(pagination.activeItemStyle)
76
+ : {};
77
+ const disabledStyle = disabled
78
+ ? this.getStyle(pagination.disabledItemStyle)
79
+ : {};
80
+
81
+ return {
82
+ ...baseStyle,
83
+ ...activeStyle,
84
+ ...disabledStyle,
85
+ 'pointer-events': disabled ? 'none' : 'auto'
86
+ };
87
+ }
88
+
89
+ invokeOnPageSizeChange(pageSize: any) {
90
+ this.pageSizeChange.emit(Number(pageSize));
91
+ }
92
+
93
+ invokeGoToPage(page: number) {
94
+ if (page < 0 || page > this.pages.length - 1 || page === this.currentPage) {
95
+ return;
96
+ }
97
+
98
+ this.onGoToPage.emit(page);
99
+ }
100
+ }