@libs-ui/components-table 0.2.357-2 → 0.2.357-21

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 CHANGED
@@ -55,8 +55,8 @@ export class YourComponent {}
55
55
  class="h-full block"
56
56
  style="display: block; height: 100%"
57
57
  [headerRight]="headerStandard"
58
- [httpRequestData]="userRequestConfig()"
59
- (outSort)="handlerSort($event)"
58
+ [httpRequestData]="userRequestConfigStandard()"
59
+ (outSort)="onSort($event)"
60
60
  />
61
61
  ```
62
62
 
@@ -64,10 +64,12 @@ export class YourComponent {}
64
64
  import { LibsUiComponentsTableComponent, ITableHeaderConfig, ISortEvent } from '@libs-ui/components-table';
65
65
  import { IHttpRequestConfig } from '@libs-ui/services-http-request';
66
66
  import { IAvatarConfig } from '@libs-ui/components-avatar';
67
+ import { HttpParams } from '@angular/common/http';
67
68
  import { signal } from '@angular/core';
68
69
  import { of } from 'rxjs';
69
70
 
70
- readonly headerStandard: Array<ITableHeaderConfig> = [
71
+ // 1. Cấu hình cột hiển thị — dùng cho [headerRight]="headerStandard"
72
+ readonly headerStandard: ITableHeaderConfig[] = [
71
73
  {
72
74
  label: 'ID',
73
75
  orderby: 'id',
@@ -84,17 +86,16 @@ readonly headerStandard: Array<ITableHeaderConfig> = [
84
86
  field: 'staff_info',
85
87
  instance: 'avatar',
86
88
  getAvatarConfig: (data) => {
87
- // data.value có thể là Signal (data wrap qua convertObjectToSignal / httpRequestData)
88
- // hoặc object thườngunwrap an toàn; fallback lấy từ row (data.item).
89
- const unwrap = (v: unknown) => (typeof v === 'function' ? (v as () => unknown)() : v);
90
- const staffInfo = unwrap((unwrap(data.item) as Record<string, unknown>)?.['staff_info']) ?? unwrap(data.value);
91
- // Không có dữ liệu avatar → trả false để ẩn HẲN ô (trả undefined sẽ bị render thành icon rỗng).
92
- if (!staffInfo) return of(false as unknown as IAvatarConfig);
93
- // link_avatar rỗng/sai → avatar tự fallback sang chữ initials nhờ idGenColor + textAvatar.
89
+ const staffInfo = data.value();
90
+ // Không staff_infoẩn avatar (trả false để @if của lib ẩn ô)
91
+ if (!staffInfo) {
92
+ return of(false as unknown as IAvatarConfig);
93
+ }
94
+ // staff_info → hiển thị avatar (linkAvatar rỗng thì tự fallback chữ initials)
94
95
  return of({
95
- linkAvatar: (staffInfo as { link_avatar?: string }).link_avatar,
96
- idGenColor: (staffInfo as { username?: string }).username,
97
- textAvatar: (staffInfo as { name?: string }).name,
96
+ linkAvatar: staffInfo.link_avatar,
97
+ idGenColor: staffInfo.username,
98
+ textAvatar: staffInfo.name,
98
99
  size: 32,
99
100
  });
100
101
  },
@@ -108,17 +109,38 @@ readonly headerStandard: Array<ITableHeaderConfig> = [
108
109
  ngClass: { 'w-1/5': true },
109
110
  colTemplateConfig: [{ cssWrapper: 'font-semibold', fieldsConfig: [{ field: 'name' }] }],
110
111
  },
112
+ {
113
+ label: 'Role',
114
+ ngClass: { 'w-1/5': true },
115
+ colTemplateConfig: [{ cssWrapper: '', fieldsConfig: [{ field: 'role' }] }],
116
+ },
117
+ {
118
+ label: 'Status',
119
+ ngClass: { 'w-1/5': true },
120
+ colTemplateConfig: [{
121
+ cssWrapper: '',
122
+ fieldsConfig: [{
123
+ field: 'status',
124
+ instance: 'button-status',
125
+ parseValue: (data) => of({
126
+ type: data.value === 'Active' ? 'green' : 'gray',
127
+ label: data.value,
128
+ }),
129
+ }],
130
+ }],
131
+ },
111
132
  ];
112
133
 
113
- readonly userRequestConfig = signal<IHttpRequestConfig>({
114
- objectInstance: new MockUserTableService(),
134
+ // 2. Cấu hình service gọi API lấy dữ liệu — dùng cho [httpRequestData]="userRequestConfigStandard()"
135
+ readonly userRequestConfigStandard = signal<IHttpRequestConfig>({
136
+ objectInstance: new UserTableService(), // service có method list() trả về danh sách
115
137
  functionName: 'list',
116
138
  argumentsValue: [new HttpParams()],
117
139
  });
118
140
 
119
- handlerSort(event: ISortEvent): void {
120
- event.stopPropagation();
121
- console.log('Sort changed:', event.sort);
141
+ // 3. Handler nhận sự kiện sort — dùng cho (outSort)="onSort($event)"
142
+ onSort(event: ISortEvent): void {
143
+ console.log('Sorting changed:', event);
122
144
  }
123
145
  ```
124
146
 
@@ -138,22 +160,33 @@ handlerSort(event: ISortEvent): void {
138
160
  showInputGotoPage: true,
139
161
  numberPageDisplay: 5
140
162
  }"
141
- (outSort)="handlerSort($event)"
163
+ (outSort)="onSort($event)"
142
164
  />
143
165
  ```
144
166
 
145
167
  ```typescript
146
- import { LibsUiComponentsTableComponent, ITableHeaderConfig } from '@libs-ui/components-table';
147
- import { signal } from '@angular/core';
168
+ import { LibsUiComponentsTableComponent, ITableHeaderConfig, ISortEvent } from '@libs-ui/components-table';
148
169
  import { HttpParams } from '@angular/common/http';
170
+ import { signal } from '@angular/core';
149
171
 
172
+ // [headerRight]="headerStandard" — cấu hình cột dùng lại từ ví dụ "Standard Table" phía trên.
173
+
174
+ // 1. Cấu hình service — activityLoadData="'click-pagination'" bật chế độ phân trang.
175
+ // service.list() nhận params { page, pageSize } và trả về paging metadata.
150
176
  readonly userRequestConfigPagination = signal({
151
- objectInstance: new MockUserTableService(),
177
+ objectInstance: new UserTableService(),
152
178
  functionName: 'list',
153
179
  argumentsValue: [new HttpParams()],
154
180
  });
155
- // MockUserTableService.list() nhận params { page, pageSize }
156
- // phải trả về: { data: [...], paging: { page, pageSize, total_items, total_pages } }
181
+
182
+ // 2. paginationSetting cấu hình trực tiếp trên template, tuỳ chỉnh giao diện phân trang.
183
+ // [paginationSetting]="{ showTotalPage: true, showInputGotoPage: true, numberPageDisplay: 5 }"
184
+ // showTotalPage: hiện tổng số trang | showInputGotoPage: hiện ô nhập trang | numberPageDisplay: số nút trang
185
+
186
+ // 3. Handler nhận sự kiện sort — dùng cho (outSort)="onSort($event)"
187
+ onSort(event: ISortEvent): void {
188
+ console.log('Sorting changed:', event);
189
+ }
157
190
  ```
158
191
 
159
192
  ---
@@ -257,38 +290,29 @@ import {
257
290
  ITableHeaderConfig,
258
291
  IConfigTemplateItemCollapseExpand,
259
292
  TYPE_NEW_DATA_TABLE,
293
+ TYPE_ITEM_DATA_TABLE,
260
294
  } from '@libs-ui/components-table';
261
295
  import { convertObjectToSignal } from '@libs-ui/utils';
262
- import { of } from 'rxjs';
296
+ import { TYPE_OBJECT } from '@libs-ui/interfaces-types';
263
297
 
264
- kpiHeadersLeft: Array<ITableHeaderConfig> = KpiReportHeadersConfig(toggleExpand).slice(0, 1);
265
- kpiHeadersRight: Array<ITableHeaderConfig> = KpiReportHeadersConfig(toggleExpand).slice(1);
266
- kpiExpandConfig: IConfigTemplateItemCollapseExpand = {
267
- fieldGetDataExpand: 'expand_data',
268
- colTemplateConfig: [{
269
- cssWrapper: 'flex w-full',
270
- fieldsConfig: [{
271
- field: 'children',
272
- parseValue: (data) => {
273
- const rows = data.value() || [];
274
- const html = rows.map((r: any) => `<div class="p-2">${r.name}</div>`).join('');
275
- return of(`<div class="bg-gray-50 p-4">${html}</div>`);
276
- },
277
- }],
278
- }],
279
- };
280
- kpiData: TYPE_NEW_DATA_TABLE = {
281
- data: convertObjectToSignal([
282
- { id: 1, name: 'KPI 1', specificExpand: false, expand_data: [{ children: [{ name: 'Sub KPI A' }] }] },
283
- ]),
284
- };
298
+ // Cấu hình cột đầy đủ (KpiReportHeadersConfig) — cột đầu ghim trái, còn lại cuộn phải.
299
+ // onToggleExpand: bật/tắt cờ 'specificExpand' trên item khi user bấm nút mở rộng dòng.
300
+ const kpiAllHeaders = KpiReportHeadersConfig((item: TYPE_ITEM_DATA_TABLE) => {
301
+ item.update((val: TYPE_OBJECT) => ({ ...val, specificExpand: !val['specificExpand'] }));
302
+ });
285
303
 
286
- toggleExpand(item: any): void {
287
- item.update((val: any) => ({ ...val, specificExpand: !val.specificExpand }));
288
- }
304
+ // [headerLeft]="kpiHeadersLeft" / [headerRight]="kpiHeadersRight"
305
+ readonly kpiHeadersLeft: Array<ITableHeaderConfig> = kpiAllHeaders.slice(0, 1);
306
+ readonly kpiHeadersRight: Array<ITableHeaderConfig> = kpiAllHeaders.slice(1);
307
+
308
+ // [configTemplateItemCollapseExpand]="kpiExpandConfig" — cấu hình render HTML mở rộng của dòng
309
+ readonly kpiExpandConfig: IConfigTemplateItemCollapseExpand = KpiReportExpandConfig();
310
+
311
+ // [newData]="kpiData"
312
+ readonly kpiData: TYPE_NEW_DATA_TABLE = { data: convertObjectToSignal(KpiReportMockData()) };
289
313
  ```
290
314
 
291
- > ⚠️ **Collapse/Expand**: Mỗi item trong `data` phải có trường `specificExpand: boolean` để điều hướng state ẩn/hiện. Trường dữ liệu mở rộng được khai báo qua `fieldGetDataExpand`.
315
+ > ⚠️ **Collapse/Expand**: Mỗi item trong `data` phải có trường `specificExpand: boolean` để điều hướng state ẩn/hiện. Trường dữ liệu mở rộng được khai báo qua `fieldGetDataExpand` (mặc định `'expand_data'`).
292
316
 
293
317
  ---
294
318
 
@@ -390,31 +414,35 @@ import { LibsUiComponentsTableComponent, ITableHeaderConfig, TYPE_NEW_DATA_TABLE
390
414
  import { convertObjectToSignal } from '@libs-ui/utils';
391
415
  import { TYPE_OBJECT } from '@libs-ui/interfaces-types';
392
416
 
393
- readonly COL_COUNT = 1000;
394
- readonly ROW_COUNT = 30;
417
+ const PERF_COL_COUNT = 1000;
418
+ const PERF_ROW_COUNT = 30;
395
419
 
396
- public handlerGetWidthItem = async () => 64; // px — phải khớp min-w / max-w trong ngClass header
397
-
398
- readonly headerPerformanceStress: Array<ITableHeaderConfig> = Array.from(
399
- { length: this.COL_COUNT },
400
- (_, i) => ({
420
+ function buildPerformanceStressHeaders(): Array<ITableHeaderConfig> {
421
+ return Array.from({ length: PERF_COL_COUNT }, (_, i) => ({
401
422
  label: `C${i + 1}`,
402
423
  ngClass: { 'shrink-0 min-w-[64px] max-w-[64px]': true },
403
424
  colTemplateConfig: [{
404
425
  cssWrapper: 'text-xs font-mono truncate max-w-[64px]',
405
426
  fieldsConfig: [{ field: `f${i}` }],
406
427
  }],
407
- })
408
- );
428
+ }));
429
+ }
409
430
 
431
+ function buildPerformanceStressRows(): Array<TYPE_OBJECT> {
432
+ return Array.from({ length: PERF_ROW_COUNT }, (_, row) => {
433
+ const rowObj: TYPE_OBJECT = { id: row + 1 };
434
+ for (let c = 0; c < PERF_COL_COUNT; c++) {
435
+ rowObj[`f${c}`] = `${row + 1}-${c + 1}`;
436
+ }
437
+ return rowObj;
438
+ });
439
+ }
440
+
441
+ // ${PERF_COL_COUNT} cột × ${PERF_ROW_COUNT} dòng — đo FPS / Scripting time trong Performance.
442
+ public handlerGetWidthItem = async () => 64; // Kích thước cột px thực tế
443
+ readonly headerPerformanceStress = buildPerformanceStressHeaders();
410
444
  readonly dataPerformanceStress: TYPE_NEW_DATA_TABLE = {
411
- data: convertObjectToSignal(
412
- Array.from({ length: this.ROW_COUNT }, (_, row) => {
413
- const obj: TYPE_OBJECT = { id: row + 1 };
414
- for (let c = 0; c < this.COL_COUNT; c++) obj[`f${c}`] = `${row + 1}-${c + 1}`;
415
- return obj;
416
- })
417
- ),
445
+ data: convertObjectToSignal(buildPerformanceStressRows()),
418
446
  };
419
447
  ```
420
448
 
@@ -444,41 +472,48 @@ import { LibsUiComponentsTableComponent, ITableHeaderConfig, TYPE_NEW_DATA_TABLE
444
472
  import { convertObjectToSignal } from '@libs-ui/utils';
445
473
  import { TYPE_OBJECT } from '@libs-ui/interfaces-types';
446
474
 
447
- readonly RIGHT_COL_COUNT = 3000;
448
- readonly ROW_COUNT = 50;
449
-
450
- public handlerGetWidthItem = async () => 64;
451
-
452
- readonly headerLeftPinnedScroll: Array<ITableHeaderConfig> = [
453
- {
454
- label: 'ID',
455
- ngClass: { 'shrink-0 min-w-[80px] max-w-[80px]': true },
456
- colTemplateConfig: [{ cssWrapper: 'text-xs font-mono text-gray-500 truncate max-w-[80px]', fieldsConfig: [{ field: 'id' }] }],
457
- },
458
- {
459
- label: 'Name',
460
- ngClass: { 'shrink-0 min-w-[160px] max-w-[160px]': true },
461
- colTemplateConfig: [{ cssWrapper: 'text-xs font-semibold truncate max-w-[160px]', fieldsConfig: [{ field: 'name' }] }],
462
- },
463
- ];
475
+ const PINNED_SCROLL_RIGHT_COL_COUNT = 3000;
476
+ const PINNED_SCROLL_ROW_COUNT = 50;
477
+
478
+ function buildPinnedScrollHeaderLeft(): Array<ITableHeaderConfig> {
479
+ return [
480
+ {
481
+ label: 'ID',
482
+ ngClass: { 'shrink-0 min-w-[80px] max-w-[80px]': true },
483
+ colTemplateConfig: [{ cssWrapper: 'text-xs font-mono text-gray-500 truncate max-w-[80px]', fieldsConfig: [{ field: 'id' }] }],
484
+ },
485
+ {
486
+ label: 'Name',
487
+ ngClass: { 'shrink-0 min-w-[160px] max-w-[160px]': true },
488
+ colTemplateConfig: [{ cssWrapper: 'text-xs font-semibold truncate max-w-[160px]', fieldsConfig: [{ field: 'name' }] }],
489
+ },
490
+ ];
491
+ }
464
492
 
465
- readonly headerRightPinnedScroll: Array<ITableHeaderConfig> = Array.from(
466
- { length: this.RIGHT_COL_COUNT },
467
- (_, i) => ({
493
+ function buildPinnedScrollHeaderRight(): Array<ITableHeaderConfig> {
494
+ return Array.from({ length: PINNED_SCROLL_RIGHT_COL_COUNT }, (_, i) => ({
468
495
  label: `M${i + 1}`,
469
496
  ngClass: { 'shrink-0 min-w-[64px] max-w-[64px]': true },
470
497
  colTemplateConfig: [{ cssWrapper: 'text-xs font-mono truncate max-w-[64px]', fieldsConfig: [{ field: `m${i}` }] }],
471
- })
472
- );
498
+ }));
499
+ }
473
500
 
501
+ function buildPinnedScrollRows(): Array<TYPE_OBJECT> {
502
+ return Array.from({ length: PINNED_SCROLL_ROW_COUNT }, (_, row) => {
503
+ const rowObj: TYPE_OBJECT = { id: row + 1, name: `User ${row + 1}` };
504
+ for (let c = 0; c < PINNED_SCROLL_RIGHT_COL_COUNT; c++) {
505
+ rowObj[`m${c}`] = `${row + 1}-${c + 1}`;
506
+ }
507
+ return rowObj;
508
+ });
509
+ }
510
+
511
+ // Ghim 2 cột bên trái (ID, Name) + ${PINNED_SCROLL_RIGHT_COL_COUNT} cột bên phải dùng virtual scroll cột ngang.
512
+ public handlerGetWidthItem = async () => 64; // Kích thước cột px thực tế (khớp min-w / max-w trong header)
513
+ readonly headerLeftPinnedScroll = buildPinnedScrollHeaderLeft();
514
+ readonly headerRightPinnedScroll = buildPinnedScrollHeaderRight();
474
515
  readonly dataPinnedScroll: TYPE_NEW_DATA_TABLE = {
475
- data: convertObjectToSignal(
476
- Array.from({ length: this.ROW_COUNT }, (_, row) => {
477
- const obj: TYPE_OBJECT = { id: row + 1, name: `User ${row + 1}` };
478
- for (let c = 0; c < this.RIGHT_COL_COUNT; c++) obj[`m${c}`] = `${row + 1}-${c + 1}`;
479
- return obj;
480
- })
481
- ),
516
+ data: convertObjectToSignal(buildPinnedScrollRows()),
482
517
  };
483
518
  ```
484
519
 
@@ -750,14 +785,6 @@ const requestConfig: IHttpRequestConfig = {
750
785
 
751
786
  ---
752
787
 
753
- ## Demo
754
-
755
- ```bash
756
- npx nx serve core-ui
757
- ```
758
-
759
- Truy cập: `http://localhost:4500/table`
760
-
761
788
  ## License
762
789
 
763
790
  MIT
@@ -1,2 +1,2 @@
1
1
  export {};
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnVuY3Rpb24tY29udHJvbC1ldmVudC5pbnRlcmZhY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvdGFibGUvc3JjL2ludGVyZmFjZXMvZnVuY3Rpb24tY29udHJvbC1ldmVudC5pbnRlcmZhY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFdyaXRhYmxlU2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBWaXJ0dWFsU2Nyb2xsZXJDb21wb25lbnQgfSBmcm9tICdAaWhhcmJlY2svbmd4LXZpcnR1YWwtc2Nyb2xsZXInO1xuaW1wb3J0IHsgVFlQRV9PQkpFQ1QgfSBmcm9tICdAbGlicy11aS9pbnRlcmZhY2VzLXR5cGVzJztcbmltcG9ydCB7IFRZUEVfREFUQV9UQUJMRSwgVFlQRV9JVEVNX0RBVEFfVEFCTEUgfSBmcm9tICcuL3RhYmxlLnR5cGUnO1xuXG5leHBvcnQgaW50ZXJmYWNlIElUYWJsZUZ1bmN0aW9uQ29udHJvbEV2ZW50IHtcbiAgcmVzZXQ6IChjbGVhclNvcnQ/OiBib29sZWFuKSA9PiB2b2lkO1xuICBjYWxsQXBpQnlTZXJ2aWNlOiAoZmlyc3RDYWxsOiBib29sZWFuLCByZXNldEFmdGVyQ2FsbEFwaT86IGJvb2xlYW4sIGlzQ2hhbmdlUGFnZT86IGJvb2xlYW4pID0+IHZvaWQ7XG4gIHJlc2V0U2Nyb2xsOiAoc2Nyb2xsUG9zaXRpb25MZWZ0PzogbnVtYmVyLCBzY3JvbGxQb3NpdGlvblRvcD86IG51bWJlcikgPT4gdm9pZDtcbiAgcmVtb3ZlSXRlbXM6IChrZXlzOiBBcnJheTx1bmtub3duPiwgaWdub3JlUmVDYWxsRm9vdGVyPzogYm9vbGVhbiwgYXV0b0NvdW50TmV3SXRlbT86IGJvb2xlYW4pID0+IHZvaWQ7XG4gIGFkZEl0ZW1zOiAoaXRlbXM6IFRZUEVfREFUQV9UQUJMRSwgYWRkRmlyc3Q6IGJvb2xlYW4sIGlnbm9yZUhpZ2h0TGlnaHQ/OiBib29sZWFuLCBpZ25vcmVSZUNhbGxGb290ZXI/OiBib29sZWFuLCBhdXRvQ291bnROZXdJdGVtPzogYm9vbGVhbikgPT4gdm9pZDtcbiAgZ2V0U2Nyb2xsQ29udGFpbmVyOiAoKSA9PiBWaXJ0dWFsU2Nyb2xsZXJDb21wb25lbnQ7XG4gIGhpZGVUb29sQmFyQm90dG9tOiAoKSA9PiB2b2lkO1xuICByZXNldEtleVNlbGVjdGVkOiAoKSA9PiB2b2lkO1xuICBkZXRlY3RDaGFuZ2VzOiAoKSA9PiB2b2lkO1xuICBzZXRDb3VudFBhZ2luZ1N0b3JlOiAoY291bnQ6IG51bWJlcikgPT4gdm9pZDtcbiAgZ2V0Q291bnQ6ICgpID0+IG51bWJlcjtcbiAgZ2V0U3RvcmU6ICgpID0+IEFycmF5PFdyaXRhYmxlU2lnbmFsPFRZUEVfT0JKRUNUPj47XG4gIGdldEl0ZW1zOiAoKSA9PiBBcnJheTxXcml0YWJsZVNpZ25hbDxUWVBFX09CSkVDVD4+O1xuICBoaWdodExpZ2h0SXRlbTogKGl0ZW1zOiBBcnJheTxUWVBFX0lURU1fREFUQV9UQUJMRT4pID0+IFByb21pc2U8dm9pZD47XG4gIGNoYW5nZVBhZ2luYXRpb246IChwYWdlPzpudW1iZXIpID0+IFByb21pc2U8dm9pZD47XG59XG4iXX0=
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnVuY3Rpb24tY29udHJvbC1ldmVudC5pbnRlcmZhY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvdGFibGUvc3JjL2ludGVyZmFjZXMvZnVuY3Rpb24tY29udHJvbC1ldmVudC5pbnRlcmZhY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFdyaXRhYmxlU2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBWaXJ0dWFsU2Nyb2xsZXJDb21wb25lbnQgfSBmcm9tICdAaWhhcmJlY2svbmd4LXZpcnR1YWwtc2Nyb2xsZXInO1xuaW1wb3J0IHsgVFlQRV9PQkpFQ1QgfSBmcm9tICdAbGlicy11aS9pbnRlcmZhY2VzLXR5cGVzJztcbmltcG9ydCB7IFRZUEVfREFUQV9UQUJMRSwgVFlQRV9JVEVNX0RBVEFfVEFCTEUgfSBmcm9tICcuL3RhYmxlLnR5cGUnO1xuXG5leHBvcnQgaW50ZXJmYWNlIElUYWJsZUZ1bmN0aW9uQ29udHJvbEV2ZW50IHtcbiAgcmVzZXQ6IChjbGVhclNvcnQ/OiBib29sZWFuKSA9PiB2b2lkO1xuICBjYWxsQXBpQnlTZXJ2aWNlOiAoZmlyc3RDYWxsOiBib29sZWFuLCByZXNldEFmdGVyQ2FsbEFwaT86IGJvb2xlYW4sIGlzQ2hhbmdlUGFnZT86IGJvb2xlYW4pID0+IHZvaWQ7XG4gIHJlc2V0U2Nyb2xsOiAoc2Nyb2xsUG9zaXRpb25MZWZ0PzogbnVtYmVyLCBzY3JvbGxQb3NpdGlvblRvcD86IG51bWJlcikgPT4gdm9pZDtcbiAgcmVtb3ZlSXRlbXM6IChrZXlzOiBBcnJheTx1bmtub3duPiwgaWdub3JlUmVDYWxsRm9vdGVyPzogYm9vbGVhbiwgYXV0b0NvdW50TmV3SXRlbT86IGJvb2xlYW4pID0+IHZvaWQ7XG4gIGFkZEl0ZW1zOiAoaXRlbXM6IFRZUEVfREFUQV9UQUJMRSwgYWRkRmlyc3Q6IGJvb2xlYW4sIGlnbm9yZUhpZ2h0TGlnaHQ/OiBib29sZWFuLCBpZ25vcmVSZUNhbGxGb290ZXI/OiBib29sZWFuLCBhdXRvQ291bnROZXdJdGVtPzogYm9vbGVhbikgPT4gdm9pZDtcbiAgZ2V0U2Nyb2xsQ29udGFpbmVyOiAoKSA9PiBWaXJ0dWFsU2Nyb2xsZXJDb21wb25lbnQ7XG4gIGhpZGVUb29sQmFyQm90dG9tOiAoKSA9PiB2b2lkO1xuICByZXNldEtleVNlbGVjdGVkOiAoKSA9PiB2b2lkO1xuICBkZXRlY3RDaGFuZ2VzOiAoKSA9PiB2b2lkO1xuICBzZXRDb3VudFBhZ2luZ1N0b3JlOiAoY291bnQ6IG51bWJlcikgPT4gdm9pZDtcbiAgZ2V0Q291bnQ6ICgpID0+IG51bWJlcjtcbiAgZ2V0U3RvcmU6ICgpID0+IEFycmF5PFdyaXRhYmxlU2lnbmFsPFRZUEVfT0JKRUNUPj47XG4gIGdldEl0ZW1zOiAoKSA9PiBBcnJheTxXcml0YWJsZVNpZ25hbDxUWVBFX09CSkVDVD4+O1xuICBoaWdodExpZ2h0SXRlbTogKGl0ZW1zOiBBcnJheTxUWVBFX0lURU1fREFUQV9UQUJMRT4pID0+IFByb21pc2U8dm9pZD47XG4gIGNoYW5nZVBhZ2luYXRpb246IChwYWdlPzogbnVtYmVyKSA9PiBQcm9taXNlPHZvaWQ+O1xuICBnZXRJbmRleEJ5SXRlbTogKGl0ZW06IFRZUEVfSVRFTV9EQVRBX1RBQkxFKSA9PiBQcm9taXNlPG51bWJlcj47XG59XG4iXX0=