@libs-ui/components-table 0.2.357-0 → 0.2.357-10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,11 +86,16 @@ readonly headerStandard: Array<ITableHeaderConfig> = [
84
86
  field: 'staff_info',
85
87
  instance: 'avatar',
86
88
  getAvatarConfig: (data) => {
87
- if (!data.value()) return of(undefined as unknown as IAvatarConfig);
89
+ const staffInfo = data.value();
90
+ // Không có staff_info → ẩn avatar (trả false để @if của lib ẩn ô)
91
+ if (!staffInfo) {
92
+ return of(false as unknown as IAvatarConfig);
93
+ }
94
+ // Có staff_info → hiển thị avatar (linkAvatar rỗng thì tự fallback chữ initials)
88
95
  return of({
89
- linkAvatar: data.value().link_avatar,
90
- idGenColor: data.value().username,
91
- textAvatar: data.value().name,
96
+ linkAvatar: staffInfo.link_avatar,
97
+ idGenColor: staffInfo.username,
98
+ textAvatar: staffInfo.name,
92
99
  size: 32,
93
100
  });
94
101
  },
@@ -102,17 +109,38 @@ readonly headerStandard: Array<ITableHeaderConfig> = [
102
109
  ngClass: { 'w-1/5': true },
103
110
  colTemplateConfig: [{ cssWrapper: 'font-semibold', fieldsConfig: [{ field: 'name' }] }],
104
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
+ },
105
132
  ];
106
133
 
107
- readonly userRequestConfig = signal<IHttpRequestConfig>({
108
- 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
109
137
  functionName: 'list',
110
138
  argumentsValue: [new HttpParams()],
111
139
  });
112
140
 
113
- handlerSort(event: ISortEvent): void {
114
- event.stopPropagation();
115
- 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);
116
144
  }
117
145
  ```
118
146
 
@@ -132,22 +160,33 @@ handlerSort(event: ISortEvent): void {
132
160
  showInputGotoPage: true,
133
161
  numberPageDisplay: 5
134
162
  }"
135
- (outSort)="handlerSort($event)"
163
+ (outSort)="onSort($event)"
136
164
  />
137
165
  ```
138
166
 
139
167
  ```typescript
140
- import { LibsUiComponentsTableComponent, ITableHeaderConfig } from '@libs-ui/components-table';
141
- import { signal } from '@angular/core';
168
+ import { LibsUiComponentsTableComponent, ITableHeaderConfig, ISortEvent } from '@libs-ui/components-table';
142
169
  import { HttpParams } from '@angular/common/http';
170
+ import { signal } from '@angular/core';
143
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.
144
176
  readonly userRequestConfigPagination = signal({
145
- objectInstance: new MockUserTableService(),
177
+ objectInstance: new UserTableService(),
146
178
  functionName: 'list',
147
179
  argumentsValue: [new HttpParams()],
148
180
  });
149
- // MockUserTableService.list() nhận params { page, pageSize }
150
- // 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
+ }
151
190
  ```
152
191
 
153
192
  ---
@@ -251,38 +290,29 @@ import {
251
290
  ITableHeaderConfig,
252
291
  IConfigTemplateItemCollapseExpand,
253
292
  TYPE_NEW_DATA_TABLE,
293
+ TYPE_ITEM_DATA_TABLE,
254
294
  } from '@libs-ui/components-table';
255
295
  import { convertObjectToSignal } from '@libs-ui/utils';
256
- import { of } from 'rxjs';
296
+ import { TYPE_OBJECT } from '@libs-ui/interfaces-types';
257
297
 
258
- kpiHeadersLeft: Array<ITableHeaderConfig> = KpiReportHeadersConfig(toggleExpand).slice(0, 1);
259
- kpiHeadersRight: Array<ITableHeaderConfig> = KpiReportHeadersConfig(toggleExpand).slice(1);
260
- kpiExpandConfig: IConfigTemplateItemCollapseExpand = {
261
- fieldGetDataExpand: 'expand_data',
262
- colTemplateConfig: [{
263
- cssWrapper: 'flex w-full',
264
- fieldsConfig: [{
265
- field: 'children',
266
- parseValue: (data) => {
267
- const rows = data.value() || [];
268
- const html = rows.map((r: any) => `<div class="p-2">${r.name}</div>`).join('');
269
- return of(`<div class="bg-gray-50 p-4">${html}</div>`);
270
- },
271
- }],
272
- }],
273
- };
274
- kpiData: TYPE_NEW_DATA_TABLE = {
275
- data: convertObjectToSignal([
276
- { id: 1, name: 'KPI 1', specificExpand: false, expand_data: [{ children: [{ name: 'Sub KPI A' }] }] },
277
- ]),
278
- };
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
+ });
279
303
 
280
- toggleExpand(item: any): void {
281
- item.update((val: any) => ({ ...val, specificExpand: !val.specificExpand }));
282
- }
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()) };
283
313
  ```
284
314
 
285
- > ⚠️ **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'`).
286
316
 
287
317
  ---
288
318
 
@@ -384,31 +414,35 @@ import { LibsUiComponentsTableComponent, ITableHeaderConfig, TYPE_NEW_DATA_TABLE
384
414
  import { convertObjectToSignal } from '@libs-ui/utils';
385
415
  import { TYPE_OBJECT } from '@libs-ui/interfaces-types';
386
416
 
387
- readonly COL_COUNT = 1000;
388
- readonly ROW_COUNT = 30;
417
+ const PERF_COL_COUNT = 1000;
418
+ const PERF_ROW_COUNT = 30;
389
419
 
390
- public handlerGetWidthItem = async () => 64; // px — phải khớp min-w / max-w trong ngClass header
391
-
392
- readonly headerPerformanceStress: Array<ITableHeaderConfig> = Array.from(
393
- { length: this.COL_COUNT },
394
- (_, i) => ({
420
+ function buildPerformanceStressHeaders(): Array<ITableHeaderConfig> {
421
+ return Array.from({ length: PERF_COL_COUNT }, (_, i) => ({
395
422
  label: `C${i + 1}`,
396
423
  ngClass: { 'shrink-0 min-w-[64px] max-w-[64px]': true },
397
424
  colTemplateConfig: [{
398
425
  cssWrapper: 'text-xs font-mono truncate max-w-[64px]',
399
426
  fieldsConfig: [{ field: `f${i}` }],
400
427
  }],
401
- })
402
- );
428
+ }));
429
+ }
403
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();
404
444
  readonly dataPerformanceStress: TYPE_NEW_DATA_TABLE = {
405
- data: convertObjectToSignal(
406
- Array.from({ length: this.ROW_COUNT }, (_, row) => {
407
- const obj: TYPE_OBJECT = { id: row + 1 };
408
- for (let c = 0; c < this.COL_COUNT; c++) obj[`f${c}`] = `${row + 1}-${c + 1}`;
409
- return obj;
410
- })
411
- ),
445
+ data: convertObjectToSignal(buildPerformanceStressRows()),
412
446
  };
413
447
  ```
414
448
 
@@ -438,41 +472,48 @@ import { LibsUiComponentsTableComponent, ITableHeaderConfig, TYPE_NEW_DATA_TABLE
438
472
  import { convertObjectToSignal } from '@libs-ui/utils';
439
473
  import { TYPE_OBJECT } from '@libs-ui/interfaces-types';
440
474
 
441
- readonly RIGHT_COL_COUNT = 3000;
442
- readonly ROW_COUNT = 50;
443
-
444
- public handlerGetWidthItem = async () => 64;
445
-
446
- readonly headerLeftPinnedScroll: Array<ITableHeaderConfig> = [
447
- {
448
- label: 'ID',
449
- ngClass: { 'shrink-0 min-w-[80px] max-w-[80px]': true },
450
- colTemplateConfig: [{ cssWrapper: 'text-xs font-mono text-gray-500 truncate max-w-[80px]', fieldsConfig: [{ field: 'id' }] }],
451
- },
452
- {
453
- label: 'Name',
454
- ngClass: { 'shrink-0 min-w-[160px] max-w-[160px]': true },
455
- colTemplateConfig: [{ cssWrapper: 'text-xs font-semibold truncate max-w-[160px]', fieldsConfig: [{ field: 'name' }] }],
456
- },
457
- ];
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
+ }
458
492
 
459
- readonly headerRightPinnedScroll: Array<ITableHeaderConfig> = Array.from(
460
- { length: this.RIGHT_COL_COUNT },
461
- (_, i) => ({
493
+ function buildPinnedScrollHeaderRight(): Array<ITableHeaderConfig> {
494
+ return Array.from({ length: PINNED_SCROLL_RIGHT_COL_COUNT }, (_, i) => ({
462
495
  label: `M${i + 1}`,
463
496
  ngClass: { 'shrink-0 min-w-[64px] max-w-[64px]': true },
464
497
  colTemplateConfig: [{ cssWrapper: 'text-xs font-mono truncate max-w-[64px]', fieldsConfig: [{ field: `m${i}` }] }],
465
- })
466
- );
498
+ }));
499
+ }
467
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();
468
515
  readonly dataPinnedScroll: TYPE_NEW_DATA_TABLE = {
469
- data: convertObjectToSignal(
470
- Array.from({ length: this.ROW_COUNT }, (_, row) => {
471
- const obj: TYPE_OBJECT = { id: row + 1, name: `User ${row + 1}` };
472
- for (let c = 0; c < this.RIGHT_COL_COUNT; c++) obj[`m${c}`] = `${row + 1}-${c + 1}`;
473
- return obj;
474
- })
475
- ),
516
+ data: convertObjectToSignal(buildPinnedScrollRows()),
476
517
  };
477
518
  ```
478
519
 
@@ -744,14 +785,6 @@ const requestConfig: IHttpRequestConfig = {
744
785
 
745
786
  ---
746
787
 
747
- ## Demo
748
-
749
- ```bash
750
- npx nx serve core-ui
751
- ```
752
-
753
- Truy cập: `http://localhost:4500/table`
754
-
755
788
  ## License
756
789
 
757
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=