@c8y/ngx-components 1022.21.3 → 1022.26.1

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 (79) hide show
  1. package/asset-properties/asset-properties.model.d.ts +117 -0
  2. package/asset-properties/asset-properties.model.d.ts.map +1 -0
  3. package/asset-properties/asset-properties.service.d.ts +72 -0
  4. package/asset-properties/asset-properties.service.d.ts.map +1 -0
  5. package/asset-properties/asset-property-list/asset-property-action.directive.d.ts +11 -0
  6. package/asset-properties/asset-property-list/asset-property-action.directive.d.ts.map +1 -0
  7. package/asset-properties/asset-property-list/asset-property-icon.pipe.d.ts +11 -0
  8. package/asset-properties/asset-property-list/asset-property-icon.pipe.d.ts.map +1 -0
  9. package/asset-properties/asset-property-list/asset-property-list.component.d.ts +195 -0
  10. package/asset-properties/asset-property-list/asset-property-list.component.d.ts.map +1 -0
  11. package/asset-properties/asset-property-list/asset-property-value.pipe.d.ts +17 -0
  12. package/asset-properties/asset-property-list/asset-property-value.pipe.d.ts.map +1 -0
  13. package/asset-properties/asset-property-list/tree-data-source.d.ts +19 -0
  14. package/asset-properties/asset-property-list/tree-data-source.d.ts.map +1 -0
  15. package/asset-properties/asset-property-selector-drawer/asset-property-selector-drawer.component.d.ts +75 -0
  16. package/asset-properties/asset-property-selector-drawer/asset-property-selector-drawer.component.d.ts.map +1 -0
  17. package/asset-properties/c8y-ngx-components-asset-properties.d.ts.map +1 -0
  18. package/asset-properties/index.d.ts +6 -0
  19. package/asset-properties/index.d.ts.map +1 -0
  20. package/core/user/user-edit-modal.component.d.ts.map +1 -1
  21. package/datapoint-explorer/view/datapoint-explorer.component.d.ts +2 -0
  22. package/datapoint-explorer/view/datapoint-explorer.component.d.ts.map +1 -1
  23. package/device-provisioned-certificates/device-tab-provisioned-certificates.component.d.ts +8 -2
  24. package/device-provisioned-certificates/device-tab-provisioned-certificates.component.d.ts.map +1 -1
  25. package/echart/charts.component.d.ts +1 -1
  26. package/echart/charts.component.d.ts.map +1 -1
  27. package/echart/index.d.ts +1 -0
  28. package/echart/index.d.ts.map +1 -1
  29. package/echart/models/datapoints-graph-widget.model.d.ts +13 -0
  30. package/echart/models/datapoints-graph-widget.model.d.ts.map +1 -1
  31. package/echart/services/chart-helpers.service.d.ts +23 -0
  32. package/echart/services/chart-helpers.service.d.ts.map +1 -0
  33. package/echart/services/echarts-options.service.d.ts +1 -1
  34. package/echart/services/echarts-options.service.d.ts.map +1 -1
  35. package/fesm2022/c8y-ngx-components-alarms.mjs +2 -2
  36. package/fesm2022/c8y-ngx-components-alarms.mjs.map +1 -1
  37. package/fesm2022/c8y-ngx-components-asset-properties.mjs +1573 -0
  38. package/fesm2022/c8y-ngx-components-asset-properties.mjs.map +1 -0
  39. package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs +22 -7
  40. package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs.map +1 -1
  41. package/fesm2022/c8y-ngx-components-device-list.mjs +2 -2
  42. package/fesm2022/c8y-ngx-components-device-list.mjs.map +1 -1
  43. package/fesm2022/c8y-ngx-components-device-provisioned-certificates.mjs +32 -18
  44. package/fesm2022/c8y-ngx-components-device-provisioned-certificates.mjs.map +1 -1
  45. package/fesm2022/c8y-ngx-components-echart-models.mjs +14 -1
  46. package/fesm2022/c8y-ngx-components-echart-models.mjs.map +1 -1
  47. package/fesm2022/c8y-ngx-components-echart.mjs +88 -29
  48. package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
  49. package/fesm2022/c8y-ngx-components-protocol-opcua.mjs +3 -3
  50. package/fesm2022/c8y-ngx-components-protocol-opcua.mjs.map +1 -1
  51. package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget.mjs +6 -0
  52. package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget.mjs.map +1 -1
  53. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs +12 -4
  54. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs.map +1 -1
  55. package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs +51 -10
  56. package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs.map +1 -1
  57. package/fesm2022/c8y-ngx-components.mjs +1 -1
  58. package/fesm2022/c8y-ngx-components.mjs.map +1 -1
  59. package/locales/de.po +38 -24
  60. package/locales/es.po +38 -24
  61. package/locales/fr.po +38 -24
  62. package/locales/ja_JP.po +37 -24
  63. package/locales/ko.po +37 -24
  64. package/locales/locales.pot +43 -12
  65. package/locales/nl.po +38 -24
  66. package/locales/pl.po +38 -24
  67. package/locales/pt_BR.po +38 -24
  68. package/locales/zh_CN.po +38 -24
  69. package/locales/zh_TW.po +38 -24
  70. package/package.json +1 -1
  71. package/widgets/definitions/html-widget/html-widget-config.factory.d.ts.map +1 -1
  72. package/widgets/implementations/datapoints-graph/datapoints-graph-config/datapoints-graph-widget-config.component.d.ts +2 -0
  73. package/widgets/implementations/datapoints-graph/datapoints-graph-config/datapoints-graph-widget-config.component.d.ts.map +1 -1
  74. package/widgets/implementations/html-widget/html-widget-properties-selector/html-widget-properties-selector.component.d.ts +17 -0
  75. package/widgets/implementations/html-widget/html-widget-properties-selector/html-widget-properties-selector.component.d.ts.map +1 -0
  76. package/widgets/implementations/html-widget/html-widget.model.d.ts +2 -2
  77. package/widgets/implementations/html-widget/html-widget.model.d.ts.map +1 -1
  78. package/widgets/implementations/html-widget/index.d.ts +1 -0
  79. package/widgets/implementations/html-widget/index.d.ts.map +1 -1
@@ -0,0 +1,1573 @@
1
+ import * as i2 from '@c8y/ngx-components';
2
+ import { gettext, AssetTypesRealtimeService, GroupService, ListGroupModule, C8yTranslatePipe, IconDirective, EmptyStateComponent, BottomDrawerRef, FormsModule as FormsModule$1 } from '@c8y/ngx-components';
3
+ import * as i0 from '@angular/core';
4
+ import { inject, Injectable, Directive, Pipe, viewChild, EventEmitter, effect, ContentChild, Output, Input, Component } from '@angular/core';
5
+ import { InventoryService } from '@c8y/client';
6
+ import { isArray, isObjectLike, isEmpty, find, forOwn, get, cloneDeep } from 'lodash-es';
7
+ import { firstValueFrom, BehaviorSubject, Subject, takeUntil, debounceTime } from 'rxjs';
8
+ import { NgIf, NgClass, NgTemplateOutlet } from '@angular/common';
9
+ import * as i1 from '@angular/forms';
10
+ import { FormsModule } from '@angular/forms';
11
+ import * as i3 from 'ngx-bootstrap/tooltip';
12
+ import { TooltipModule } from 'ngx-bootstrap/tooltip';
13
+ import * as i4 from '@angular/cdk/tree';
14
+ import { CdkTreeModule } from '@angular/cdk/tree';
15
+ import { DataSource } from '@angular/cdk/collections';
16
+
17
+ const defaultAssetPropertyListConfig = {
18
+ searchable: true,
19
+ selectMode: 'none',
20
+ expansionMode: 'expandedByDefault',
21
+ showHeader: true,
22
+ showValue: true,
23
+ showKey: true,
24
+ emptyStateContent: 'empty',
25
+ inputPropertiesHandle: 'merge'
26
+ };
27
+ const defaultAssetProperties = [
28
+ {
29
+ c8y_JsonSchema: { properties: { name: { type: 'string', label: 'Name' } } },
30
+ name: 'name',
31
+ label: 'Name',
32
+ type: 'string',
33
+ active: true,
34
+ isEditable: true,
35
+ isStandardProperty: true
36
+ },
37
+ {
38
+ c8y_JsonSchema: { properties: { id: { type: 'string', label: 'ID' } } },
39
+ name: 'id',
40
+ label: 'ID',
41
+ type: 'string',
42
+ active: true,
43
+ isEditable: false,
44
+ isStandardProperty: true
45
+ },
46
+ {
47
+ c8y_JsonSchema: {
48
+ properties: { type: { type: 'string', label: 'Type' } }
49
+ },
50
+ name: 'type',
51
+ label: 'Type',
52
+ type: 'string',
53
+ active: true,
54
+ isEditable: false,
55
+ isStandardProperty: true
56
+ },
57
+ {
58
+ c8y_JsonSchema: {
59
+ properties: { owner: { type: 'string', label: 'Owner' } }
60
+ },
61
+ name: 'owner',
62
+ label: 'Owner',
63
+ type: 'string',
64
+ isEditable: false,
65
+ isStandardProperty: true
66
+ },
67
+ {
68
+ c8y_JsonSchema: {
69
+ properties: { lastUpdated: { type: 'string', label: 'Last updated' } }
70
+ },
71
+ name: 'lastUpdated',
72
+ label: 'Last updated',
73
+ type: 'string',
74
+ isEditable: false,
75
+ isStandardProperty: true
76
+ }
77
+ ];
78
+ const deviceAssetProperties = [
79
+ {
80
+ label: 'Active alarms status',
81
+ type: 'object',
82
+ isEditable: false,
83
+ isStandardProperty: true,
84
+ name: 'c8y_ActiveAlarmsStatus',
85
+ c8y_JsonSchema: {
86
+ properties: {
87
+ c8y_ActiveAlarmsStatus: {
88
+ key: 'c8y_ActiveAlarmsStatus',
89
+ type: 'object',
90
+ label: 'Active alarms status',
91
+ properties: {
92
+ critical: {
93
+ title: 'Critical',
94
+ type: 'number'
95
+ },
96
+ major: {
97
+ title: 'Major',
98
+ type: 'number'
99
+ },
100
+ minor: {
101
+ title: 'Minor',
102
+ type: 'number'
103
+ },
104
+ warning: {
105
+ title: 'Warning',
106
+ type: 'number'
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ },
113
+ {
114
+ label: 'Address',
115
+ type: 'object',
116
+ isEditable: true,
117
+ isStandardProperty: true,
118
+ name: 'c8y_Address',
119
+ c8y_JsonSchema: {
120
+ properties: {
121
+ c8y_Address: {
122
+ key: 'c8y_Address',
123
+ type: 'object',
124
+ label: 'Address',
125
+ properties: {
126
+ street: {
127
+ title: 'Street',
128
+ type: 'string'
129
+ },
130
+ city: {
131
+ title: 'City',
132
+ type: 'string'
133
+ },
134
+ cityCode: {
135
+ title: 'City code',
136
+ type: 'string'
137
+ },
138
+ territory: {
139
+ title: 'Territory',
140
+ type: 'string'
141
+ },
142
+ region: {
143
+ title: 'Region',
144
+ type: 'string'
145
+ },
146
+ country: {
147
+ title: 'Country',
148
+ type: 'string'
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+ },
155
+ {
156
+ label: 'Agent',
157
+ type: 'object',
158
+ isEditable: true,
159
+ isStandardProperty: true,
160
+ name: 'c8y_Agent',
161
+ c8y_JsonSchema: {
162
+ properties: {
163
+ c8y_Agent: {
164
+ key: 'c8y_Agent',
165
+ type: 'object',
166
+ label: 'Agent',
167
+ properties: {
168
+ name: {
169
+ title: 'Name',
170
+ type: 'string'
171
+ },
172
+ version: {
173
+ title: 'Version',
174
+ type: 'string'
175
+ },
176
+ url: {
177
+ title: 'URL',
178
+ type: 'string'
179
+ },
180
+ maintainer: {
181
+ title: 'Maintainer',
182
+ type: 'string'
183
+ }
184
+ }
185
+ }
186
+ }
187
+ }
188
+ },
189
+ {
190
+ label: 'Availability',
191
+ type: 'object',
192
+ isEditable: false,
193
+ isStandardProperty: true,
194
+ name: 'c8y_Availability',
195
+ c8y_JsonSchema: {
196
+ properties: {
197
+ c8y_Availability: {
198
+ key: 'c8y_Availability',
199
+ type: 'object',
200
+ label: 'Availability',
201
+ properties: {
202
+ status: {
203
+ title: 'Status',
204
+ type: 'string'
205
+ },
206
+ lastMessage: {
207
+ title: 'Last message',
208
+ type: 'string',
209
+ printFormat: 'datetime'
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
215
+ },
216
+ {
217
+ label: 'Connection',
218
+ type: 'object',
219
+ isEditable: false,
220
+ isStandardProperty: true,
221
+ name: 'c8y_Connection',
222
+ c8y_JsonSchema: {
223
+ properties: {
224
+ c8y_Connection: {
225
+ key: 'c8y_Connection',
226
+ type: 'object',
227
+ label: 'Connection',
228
+ properties: {
229
+ status: {
230
+ title: 'Status',
231
+ type: 'string'
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }
237
+ },
238
+ {
239
+ label: 'Communication mode',
240
+ type: 'object',
241
+ isEditable: true,
242
+ isStandardProperty: true,
243
+ name: 'c8y_CommunicationMode',
244
+ c8y_JsonSchema: {
245
+ properties: {
246
+ c8y_CommunicationMode: {
247
+ key: 'c8y_CommunicationMode',
248
+ type: 'object',
249
+ label: 'Communication mode',
250
+ properties: {
251
+ mode: {
252
+ title: 'Mode',
253
+ type: 'string'
254
+ }
255
+ }
256
+ }
257
+ }
258
+ }
259
+ },
260
+ {
261
+ label: 'Firmware',
262
+ type: 'object',
263
+ isEditable: false,
264
+ isStandardProperty: true,
265
+ name: 'c8y_Firmware',
266
+ c8y_JsonSchema: {
267
+ properties: {
268
+ c8y_Firmware: {
269
+ key: 'c8y_Firmware',
270
+ type: 'object',
271
+ label: 'Firmware',
272
+ properties: {
273
+ moduleVersion: {
274
+ title: 'Module version',
275
+ type: 'string'
276
+ },
277
+ name: {
278
+ title: 'Name',
279
+ type: 'string'
280
+ },
281
+ version: {
282
+ title: 'Version',
283
+ type: 'string'
284
+ },
285
+ url: {
286
+ title: 'URL',
287
+ type: ['string', 'null']
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+ },
294
+ {
295
+ label: 'Hardware',
296
+ type: 'object',
297
+ isEditable: true,
298
+ isStandardProperty: true,
299
+ name: 'c8y_Hardware',
300
+ c8y_JsonSchema: {
301
+ properties: {
302
+ c8y_Hardware: {
303
+ key: 'c8y_Hardware',
304
+ type: 'object',
305
+ label: 'Hardware',
306
+ properties: {
307
+ model: {
308
+ title: 'Model',
309
+ type: 'string'
310
+ },
311
+ serialNumber: {
312
+ title: 'Serial number',
313
+ type: 'string'
314
+ },
315
+ revision: {
316
+ title: 'Revision',
317
+ type: 'string'
318
+ }
319
+ }
320
+ }
321
+ }
322
+ }
323
+ },
324
+ {
325
+ label: 'LPWAN device',
326
+ type: 'object',
327
+ isEditable: false,
328
+ isStandardProperty: true,
329
+ name: 'c8y_LpwanDevice',
330
+ c8y_JsonSchema: {
331
+ properties: {
332
+ c8y_LpwanDevice: {
333
+ key: 'c8y_LpwanDevice',
334
+ type: 'object',
335
+ label: 'LPWAN device',
336
+ properties: {
337
+ provisioned: {
338
+ title: 'Provisioned',
339
+ type: 'boolean'
340
+ }
341
+ }
342
+ }
343
+ }
344
+ }
345
+ },
346
+ {
347
+ label: 'Mobile',
348
+ type: 'object',
349
+ isEditable: true,
350
+ isStandardProperty: true,
351
+ name: 'c8y_Mobile',
352
+ c8y_JsonSchema: {
353
+ properties: {
354
+ c8y_Mobile: {
355
+ key: 'c8y_Mobile',
356
+ type: 'object',
357
+ label: 'Mobile',
358
+ properties: {
359
+ cellId: {
360
+ title: 'Cell ID',
361
+ type: ['string', 'null']
362
+ },
363
+ connType: {
364
+ title: 'Connection type',
365
+ type: 'string',
366
+ readOnly: true
367
+ },
368
+ currentOperator: {
369
+ title: 'Current operator',
370
+ type: 'string',
371
+ readOnly: true
372
+ },
373
+ currentBand: {
374
+ title: 'Current band',
375
+ type: 'string',
376
+ readOnly: true
377
+ },
378
+ ecn0: {
379
+ title: 'ECN0',
380
+ type: 'string',
381
+ readOnly: true
382
+ },
383
+ iccid: {
384
+ title: 'ICCID',
385
+ type: ['string', 'null']
386
+ },
387
+ imei: {
388
+ title: 'IMEI',
389
+ type: ['string', 'null']
390
+ },
391
+ imsi: {
392
+ title: 'IMSI',
393
+ type: ['string', 'null']
394
+ },
395
+ lac: {
396
+ title: 'LAC',
397
+ type: ['string', 'null']
398
+ },
399
+ mcc: {
400
+ title: 'MCC',
401
+ type: ['string', 'null']
402
+ },
403
+ mnc: {
404
+ title: 'MNC',
405
+ type: ['string', 'null']
406
+ },
407
+ msisdn: {
408
+ title: 'MSISDN',
409
+ type: 'string'
410
+ },
411
+ rcsp: {
412
+ title: 'RCSP',
413
+ type: 'string',
414
+ readOnly: true
415
+ },
416
+ rscp: {
417
+ title: 'RSCP',
418
+ type: 'string',
419
+ readOnly: true
420
+ },
421
+ rsrp: {
422
+ title: 'RSRP',
423
+ type: 'string',
424
+ readOnly: true
425
+ },
426
+ rsrq: {
427
+ title: 'RSRQ',
428
+ type: 'string',
429
+ readOnly: true
430
+ },
431
+ rssi: {
432
+ title: 'RSSI',
433
+ type: 'string',
434
+ readOnly: true
435
+ }
436
+ }
437
+ }
438
+ }
439
+ }
440
+ },
441
+ {
442
+ name: 'c8y_Notes',
443
+ label: 'Notes',
444
+ type: 'string',
445
+ isEditable: true,
446
+ isStandardProperty: true,
447
+ c8y_JsonSchema: {
448
+ properties: {
449
+ c8y_Notes: {
450
+ type: 'string',
451
+ label: 'Notes',
452
+ 'x-schema-form': {
453
+ type: 'textarea'
454
+ }
455
+ }
456
+ }
457
+ }
458
+ },
459
+ {
460
+ label: 'Position',
461
+ type: 'object',
462
+ isEditable: true,
463
+ isStandardProperty: true,
464
+ name: 'c8y_Position',
465
+ c8y_JsonSchema: {
466
+ properties: {
467
+ c8y_Position: {
468
+ key: 'c8y_Position',
469
+ type: 'object',
470
+ label: 'Position',
471
+ properties: {
472
+ lat: {
473
+ title: 'Latitude',
474
+ type: 'number'
475
+ },
476
+ lng: {
477
+ title: 'Longitude',
478
+ type: 'number'
479
+ },
480
+ alt: {
481
+ title: 'Altitude',
482
+ type: 'number'
483
+ }
484
+ }
485
+ }
486
+ }
487
+ }
488
+ },
489
+ {
490
+ label: 'Required availability',
491
+ type: 'object',
492
+ isEditable: true,
493
+ isStandardProperty: true,
494
+ name: 'c8y_RequiredAvailability',
495
+ c8y_JsonSchema: {
496
+ properties: {
497
+ c8y_RequiredAvailability: {
498
+ key: 'c8y_RequiredAvailability',
499
+ type: 'object',
500
+ label: 'Required availability',
501
+ properties: {
502
+ responseInterval: {
503
+ title: 'Response interval',
504
+ description: 'Takes a value between -32768 and 32767 minutes (a negative value indicates that the device is under maintenance).',
505
+ type: 'integer',
506
+ minimum: -32768,
507
+ maximum: 32767
508
+ }
509
+ }
510
+ }
511
+ }
512
+ }
513
+ },
514
+ {
515
+ label: 'Software',
516
+ type: 'object',
517
+ isEditable: false,
518
+ isStandardProperty: true,
519
+ name: 'c8y_Software',
520
+ c8y_JsonSchema: {
521
+ properties: {
522
+ c8y_Software: {
523
+ key: 'c8y_Software',
524
+ type: 'object',
525
+ label: 'Software',
526
+ properties: {
527
+ name: {
528
+ title: 'Name',
529
+ type: 'string'
530
+ },
531
+ version: {
532
+ title: 'Version',
533
+ type: 'string'
534
+ },
535
+ url: {
536
+ title: 'URL',
537
+ type: ['string', 'null']
538
+ }
539
+ }
540
+ }
541
+ }
542
+ }
543
+ },
544
+ {
545
+ label: 'Network',
546
+ type: 'object',
547
+ isEditable: true,
548
+ isStandardProperty: true,
549
+ name: 'c8y_Network',
550
+ c8y_JsonSchema: {
551
+ properties: {
552
+ c8y_Network: {
553
+ key: 'c8y_Network',
554
+ type: 'object',
555
+ label: 'Network',
556
+ properties: {
557
+ c8y_DHCP: {
558
+ title: 'DHCP',
559
+ type: 'object',
560
+ printFormat: 'hidden',
561
+ name: 'c8y_DHCP',
562
+ properties: {
563
+ addressRange: {
564
+ title: 'Address range',
565
+ type: 'object',
566
+ name: 'addressRange',
567
+ printFormat: 'hidden',
568
+ properties: {
569
+ start: {
570
+ title: 'Start',
571
+ type: 'string'
572
+ },
573
+ end: {
574
+ title: 'End',
575
+ type: 'string'
576
+ }
577
+ }
578
+ },
579
+ dns1: {
580
+ title: 'DNS 1',
581
+ type: 'string'
582
+ },
583
+ dns2: {
584
+ title: 'DNS 2',
585
+ type: 'string'
586
+ },
587
+ enabled: {
588
+ title: 'Enabled',
589
+ type: 'integer'
590
+ }
591
+ }
592
+ },
593
+ c8y_LAN: {
594
+ title: 'LAN',
595
+ type: 'object',
596
+ name: 'c8y_LAN',
597
+ printFormat: 'hidden',
598
+ properties: {
599
+ enabled: {
600
+ title: 'Enabled',
601
+ type: 'integer'
602
+ },
603
+ ip: {
604
+ title: 'IP',
605
+ type: 'string'
606
+ },
607
+ mac: {
608
+ title: 'MAC',
609
+ type: 'string'
610
+ },
611
+ name: {
612
+ title: 'Name',
613
+ type: 'string'
614
+ },
615
+ netmask: {
616
+ title: 'Netmask',
617
+ type: 'string'
618
+ }
619
+ }
620
+ },
621
+ c8y_WAN: {
622
+ title: 'WAN',
623
+ type: 'object',
624
+ name: 'c8y_WAN',
625
+ printFormat: 'hidden',
626
+ properties: {
627
+ apn: {
628
+ title: 'APN',
629
+ type: 'string'
630
+ },
631
+ authType: {
632
+ title: 'Auth type',
633
+ type: 'string'
634
+ },
635
+ ip: {
636
+ title: 'IP',
637
+ type: 'string'
638
+ },
639
+ password: {
640
+ title: 'Password',
641
+ type: 'string'
642
+ },
643
+ simStatus: {
644
+ title: 'SIM status',
645
+ type: 'string'
646
+ },
647
+ username: {
648
+ title: 'Username',
649
+ type: 'string'
650
+ }
651
+ }
652
+ }
653
+ }
654
+ }
655
+ }
656
+ }
657
+ }
658
+ ];
659
+ const RESULT_TYPES = {
660
+ VALUE: { name: 'VALUE', value: 1, label: gettext('Only value') },
661
+ VALUE_UNIT: { name: 'VALUE_UNIT', value: 2, label: gettext('Value and unit') },
662
+ VALUE_UNIT_TIME: { name: 'VALUE_UNIT_TIME', value: 3, label: gettext('Value, unit and time') }
663
+ };
664
+
665
+ /**
666
+ * Service for managing asset properties.
667
+ */
668
+ class AssetPropertiesService {
669
+ constructor() {
670
+ this.FRAGMENTS_TO_OMIT = [
671
+ 'additionParents',
672
+ 'assetParents',
673
+ 'deviceParents',
674
+ 'childAdditions',
675
+ 'childAssets',
676
+ 'childDevices',
677
+ 'c8y_IsDevice',
678
+ '__children',
679
+ 'c8y_ui',
680
+ 'self',
681
+ 'parent',
682
+ 'c8y_DataPoint',
683
+ 'c8y_Kpi_Migrated',
684
+ /^c8y_Dashboard!\d+/
685
+ ];
686
+ this.inventoryService = inject(InventoryService);
687
+ this.assetTypesRealtimeService = inject(AssetTypesRealtimeService);
688
+ this.groupService = inject(GroupService);
689
+ }
690
+ /**
691
+ * Retrieves custom properties for an asset from asset library.
692
+ * @param asset The asset for which to retrieve custom properties.
693
+ * @returns A promise resolving to the list of custom properties.
694
+ */
695
+ async getCustomProperties(asset) {
696
+ if (asset && asset.type) {
697
+ const assetType = await firstValueFrom(this.assetTypesRealtimeService.getAssetTypeByName$(asset.type));
698
+ if (assetType) {
699
+ const { data } = await this.inventoryService.childAdditionsList(assetType, {
700
+ pageSize: 2000,
701
+ query: "$filter=(has('c8y_IsAssetProperty'))"
702
+ });
703
+ return data;
704
+ }
705
+ }
706
+ return [];
707
+ }
708
+ /**
709
+ * Retrieves the initial set of properties for an asset, based on its type.
710
+ * @param asset The asset for which to retrieve properties.
711
+ * @returns A promise resolving to the list of initial properties.
712
+ */
713
+ async getInitialProperties(asset) {
714
+ if (!asset) {
715
+ return [];
716
+ }
717
+ else if (this.groupService.isDevice(asset)) {
718
+ return await this.getDeviceProperties(asset);
719
+ }
720
+ else if (this.groupService.isGroup(asset) && !this.groupService.isAsset(asset)) {
721
+ return await this.getGroupProperties(asset);
722
+ }
723
+ else if (this.groupService.isAsset(asset)) {
724
+ return await this.getAssetProperties(asset);
725
+ }
726
+ return [];
727
+ }
728
+ /**
729
+ * Retrieves properties for a device asset.
730
+ * @param asset The device asset for which to retrieve properties.
731
+ * @returns A promise resolving to the list of device properties.
732
+ */
733
+ async getDeviceProperties(asset) {
734
+ return this.getManagedObjectProperties(asset);
735
+ }
736
+ /**
737
+ * Retrieves properties for a group asset.
738
+ * @param asset The group asset for which to retrieve properties.
739
+ * @returns A promise resolving to the list of group properties.
740
+ */
741
+ async getGroupProperties(asset) {
742
+ return this.getManagedObjectProperties(asset);
743
+ }
744
+ /**
745
+ * Retrieves properties for a regular asset.
746
+ * @param asset The asset for which to retrieve properties.
747
+ * @returns A promise resolving to the list of asset properties.
748
+ */
749
+ async getAssetProperties(asset) {
750
+ const customProperties = this.categorizeCustomProperties(await this.getCustomProperties(asset)) || [];
751
+ return customProperties;
752
+ }
753
+ /**
754
+ * Categorizes custom properties into simple and complex types.
755
+ * @param properties The custom properties to categorize.
756
+ * @returns The categorized custom properties.
757
+ */
758
+ categorizeCustomProperties(properties) {
759
+ const { simple, complex } = properties.reduce((acc, property) => {
760
+ const schema = property.c8y_JsonSchema.properties[property.name];
761
+ if (schema.type === 'object') {
762
+ acc.complex.push(property);
763
+ }
764
+ else {
765
+ acc.simple.push(property);
766
+ }
767
+ return acc;
768
+ }, { simple: [], complex: [] });
769
+ return [...simple, ...complex];
770
+ }
771
+ /**
772
+ * Categorizes and flattens hierarchical properties into simple and complex types.
773
+ * @param properties The hierarchical properties to categorize and flatten.
774
+ * @returns The categorized and flattened properties.
775
+ */
776
+ categorizeAndFlattenHierarchicalProperties(properties) {
777
+ const sortedProperties = [...properties].sort((a, b) => {
778
+ const aLabel = (a.label || a.name || '').toLowerCase();
779
+ const bLabel = (b.label || b.name || '').toLowerCase();
780
+ return aLabel.localeCompare(bLabel);
781
+ });
782
+ const result = sortedProperties.reduce((acc, property) => {
783
+ property.active = false;
784
+ if (this.isComplexProperty(property)) {
785
+ acc.complex.push(property);
786
+ this.addNestedProperties(property, acc.complex, true);
787
+ }
788
+ else {
789
+ acc.simple.push(property);
790
+ }
791
+ return acc;
792
+ }, { simple: [], complex: [] });
793
+ return result;
794
+ }
795
+ /**
796
+ * Checks if a property is complex (i.e., has nested properties).
797
+ * @param property The property to check.
798
+ * @returns True if the property is complex, false otherwise.
799
+ */
800
+ isComplexProperty(property) {
801
+ return (property.c8y_JsonSchema?.properties[property.name]?.type === 'object' ||
802
+ property.properties !== undefined);
803
+ }
804
+ addNestedProperties(parentProperty, complexProperties, sortChildren = false) {
805
+ const schema = parentProperty.c8y_JsonSchema?.properties[parentProperty.name];
806
+ if (!schema?.properties) {
807
+ return;
808
+ }
809
+ this.flattenProperties(schema, complexProperties, parentProperty.name, [], sortChildren);
810
+ }
811
+ flattenProperties(schema, result, parentName, parentPath = [], sortChildren = false) {
812
+ const properties = schema.properties?.[parentName]?.properties || schema.properties;
813
+ let entries = Object.entries(properties);
814
+ if (sortChildren) {
815
+ entries = entries.sort((a, b) => {
816
+ const aLabel = (a[1].title || a[1].label || a[1].name || a[0] || '').toLowerCase();
817
+ const bLabel = (b[1].title || b[1].label || b[1].name || b[0] || '').toLowerCase();
818
+ return aLabel.localeCompare(bLabel);
819
+ });
820
+ }
821
+ entries.forEach(([key, property]) => {
822
+ const path = parentPath.includes(parentName) ? parentPath : [...parentPath, parentName];
823
+ result.push({ ...property, keyPath: [...path, key] });
824
+ if (property.properties) {
825
+ this.flattenProperties({ properties: { [key]: property } }, result, key, [...path, key], sortChildren);
826
+ }
827
+ });
828
+ }
829
+ getManagedObjectProperties(asset) {
830
+ return this.extractFragments(asset);
831
+ }
832
+ extractFragments(object) {
833
+ const properties = [];
834
+ for (const [key, value] of Object.entries(object)) {
835
+ if (this.shouldSkipFragment(key) ||
836
+ isArray(value) ||
837
+ (isObjectLike(value) && isEmpty(value))) {
838
+ continue;
839
+ }
840
+ const newProp = {
841
+ label: key,
842
+ name: key,
843
+ type: isObjectLike(value) ? 'object' : 'string',
844
+ isEditable: true,
845
+ c8y_JsonSchema: {
846
+ properties: {
847
+ [key]: {
848
+ key: key,
849
+ type: isObjectLike(value) ? 'object' : 'string',
850
+ label: key,
851
+ properties: {}
852
+ }
853
+ }
854
+ }
855
+ };
856
+ if (isObjectLike(value)) {
857
+ this.addPropertyItem(newProp.c8y_JsonSchema.properties[key].properties, value);
858
+ }
859
+ properties.push(newProp);
860
+ }
861
+ return properties;
862
+ }
863
+ shouldSkipFragment(key) {
864
+ return !!find(this.FRAGMENTS_TO_OMIT, (fragmentToOmit) => {
865
+ if (fragmentToOmit instanceof RegExp) {
866
+ return fragmentToOmit.test(key);
867
+ }
868
+ return fragmentToOmit === key;
869
+ });
870
+ }
871
+ addPropertyItem(properties, object) {
872
+ if (!properties) {
873
+ return;
874
+ }
875
+ forOwn(object, (value, key) => {
876
+ properties[key] = { title: key, type: value ? typeof value : 'string' };
877
+ if (isObjectLike(value)) {
878
+ properties[key].type = 'object';
879
+ this.addPropertyItem(properties[key]['properties'], value);
880
+ }
881
+ });
882
+ }
883
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertiesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
884
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertiesService, providedIn: 'root' }); }
885
+ }
886
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertiesService, decorators: [{
887
+ type: Injectable,
888
+ args: [{
889
+ providedIn: 'root'
890
+ }]
891
+ }] });
892
+
893
+ class FlatTreeDataSource extends DataSource {
894
+ constructor() {
895
+ super();
896
+ this._dataChange = new BehaviorSubject([]);
897
+ }
898
+ get data() {
899
+ return this._dataChange.value;
900
+ }
901
+ set data(value) {
902
+ this._dataChange.next(value);
903
+ }
904
+ connect() {
905
+ return this._dataChange.asObservable();
906
+ }
907
+ disconnect() {
908
+ // No need to unsubscribe from the _dataChange subject since it's a BehaviorSubject
909
+ }
910
+ }
911
+
912
+ class AssetPropertyActionDirective {
913
+ constructor(template, elementRef, viewContainer) {
914
+ this.template = template;
915
+ this.elementRef = elementRef;
916
+ this.viewContainer = viewContainer;
917
+ }
918
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertyActionDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ElementRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
919
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: AssetPropertyActionDirective, isStandalone: true, selector: "[c8yAssetPropertyAction]", ngImport: i0 }); }
920
+ }
921
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertyActionDirective, decorators: [{
922
+ type: Directive,
923
+ args: [{
924
+ selector: '[c8yAssetPropertyAction]',
925
+ standalone: true
926
+ }]
927
+ }], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ElementRef }, { type: i0.ViewContainerRef }] });
928
+
929
+ /**
930
+ * Formats the value of an asset property.
931
+ * If the property is complex, it will be stringified.
932
+ * If the property has a keyPath, it will be used to retrieve the value from the asset.
933
+ * Otherwise, the value will be taken directly from the asset.
934
+ * If the value is null or undefined, a dash ('-') will be returned.
935
+ */
936
+ class AssetPropertyValuePipe {
937
+ constructor() {
938
+ this.assetPropertiesService = inject(AssetPropertiesService);
939
+ }
940
+ transform(property, asset) {
941
+ if (!property) {
942
+ return '-';
943
+ }
944
+ let value;
945
+ if (this.assetPropertiesService.isComplexProperty(property)) {
946
+ value = JSON.stringify(asset[property.name]);
947
+ }
948
+ else if ('keyPath' in property) {
949
+ value = get(asset, property.keyPath);
950
+ }
951
+ else {
952
+ value = asset[property.name];
953
+ }
954
+ return value ?? '-';
955
+ }
956
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertyValuePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
957
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertyValuePipe, isStandalone: true, name: "c8yAssetPropertyValue" }); }
958
+ }
959
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertyValuePipe, decorators: [{
960
+ type: Pipe,
961
+ args: [{
962
+ name: 'c8yAssetPropertyValue',
963
+ standalone: true
964
+ }]
965
+ }] });
966
+
967
+ /**
968
+ * Pipe to transform asset property types into icon names.
969
+ * Maps various property types to corresponding icon names.
970
+ */
971
+ class AssetPropertyIconPipe {
972
+ transform(type) {
973
+ switch (type) {
974
+ case 'string':
975
+ return 'paragraph';
976
+ case 'number':
977
+ return 'hashtag';
978
+ case 'boolean':
979
+ return 'true-false';
980
+ case 'date':
981
+ return 'calendar';
982
+ case 'object':
983
+ case 'c8y_JsonSchema':
984
+ return 'open-parcel';
985
+ case 'enum':
986
+ return 'content';
987
+ case 'file':
988
+ return 'file';
989
+ default:
990
+ return 'window-minimize';
991
+ }
992
+ }
993
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertyIconPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
994
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertyIconPipe, isStandalone: true, name: "c8yAssetPropertyIcon" }); }
995
+ }
996
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertyIconPipe, decorators: [{
997
+ type: Pipe,
998
+ args: [{
999
+ name: 'c8yAssetPropertyIcon',
1000
+ standalone: true
1001
+ }]
1002
+ }] });
1003
+
1004
+ /**
1005
+ * Represents a list of asset properties with hierarchical tree structure.
1006
+ */
1007
+ class AssetPropertyListComponent {
1008
+ /**
1009
+ * Constructor initializes reactive effects for expansion modes.
1010
+ */
1011
+ constructor() {
1012
+ /**
1013
+ * Reference to the CDdk tree component.
1014
+ */
1015
+ this.tree = viewChild('tree');
1016
+ /**
1017
+ * Configuration for the asset property list.
1018
+ */
1019
+ this.config = {};
1020
+ /**
1021
+ * Custom properties to be displayed in the list.
1022
+ */
1023
+ this.customProperties = [];
1024
+ /**
1025
+ * Emits the selected properties.
1026
+ */
1027
+ this.selectedProperties = new EventEmitter();
1028
+ /**
1029
+ * List of all properties.
1030
+ */
1031
+ this.properties = [];
1032
+ /**
1033
+ * Text input for searching properties.
1034
+ */
1035
+ this.inputText = '';
1036
+ /**
1037
+ * Data source for the tree structure.
1038
+ */
1039
+ this.dataSource = new FlatTreeDataSource();
1040
+ /**
1041
+ * Map of flat nodes for quick lookup.
1042
+ */
1043
+ this.flatNodeMap = new Map();
1044
+ /**
1045
+ * Indicates if all nodes are selected.
1046
+ */
1047
+ this.allSelected = false;
1048
+ /**
1049
+ * Indicates if the selection state is indeterminate.
1050
+ */
1051
+ this.indeterminate = false;
1052
+ /**
1053
+ * Subject for handling search input.
1054
+ */
1055
+ this.searchSubject$ = new BehaviorSubject('');
1056
+ /**
1057
+ * Subject for handling component destruction.
1058
+ */
1059
+ this.destroy$ = new Subject();
1060
+ /**
1061
+ * Service for managing asset properties.
1062
+ */
1063
+ this.assetPropertiesService = inject(AssetPropertiesService);
1064
+ effect(() => {
1065
+ if ((this.config.expansionMode === 'nonCollapsible' ||
1066
+ this.config.expansionMode === 'expandedByDefault') &&
1067
+ this.tree()) {
1068
+ queueMicrotask(() => {
1069
+ this.expandAllNodes();
1070
+ });
1071
+ }
1072
+ });
1073
+ }
1074
+ ngOnInit() {
1075
+ this.config = {
1076
+ ...defaultAssetPropertyListConfig,
1077
+ ...this.config
1078
+ };
1079
+ this.searchSubject$
1080
+ .pipe(takeUntil(this.destroy$), debounceTime(200))
1081
+ .subscribe(() => this.filterTree());
1082
+ }
1083
+ async ngOnChanges() {
1084
+ await this.updateProperties();
1085
+ }
1086
+ ngAfterViewInit() {
1087
+ if (this.config.selectMode === 'single') {
1088
+ this.selectedProperties.next([]);
1089
+ }
1090
+ }
1091
+ ngOnDestroy() {
1092
+ this.destroy$.next();
1093
+ this.destroy$.complete();
1094
+ }
1095
+ /**
1096
+ * Fetches and categorizes properties.
1097
+ * @returns A promise resolving to the list of asset properties.
1098
+ */
1099
+ async getProperties() {
1100
+ if (this.asset) {
1101
+ return this.categorizeAndFlattenHierarchicalProperties(await this.assetPropertiesService.getInitialProperties(this.asset));
1102
+ }
1103
+ else {
1104
+ if (this.config?.emptyStateContent === 'default-properties') {
1105
+ return this.categorizeAndFlattenHierarchicalProperties(defaultAssetProperties);
1106
+ }
1107
+ else if (Array.isArray(this.config?.emptyStateContent)) {
1108
+ return this.config.emptyStateContent;
1109
+ }
1110
+ }
1111
+ return [];
1112
+ }
1113
+ /**
1114
+ * Checks if a node has children.
1115
+ * @param node The node to check.
1116
+ * @returns True if the node has children.
1117
+ */
1118
+ hasChild(node) {
1119
+ return node.expandable;
1120
+ }
1121
+ /**
1122
+ * Gets the level of a node.
1123
+ * @param node The node to check.
1124
+ * @returns The level of the node.
1125
+ */
1126
+ getLevel(node) {
1127
+ return node.level;
1128
+ }
1129
+ /**
1130
+ * Gets the parent node of a given node.
1131
+ * @param node The node to check.
1132
+ * @returns The parent node or null if none exists.
1133
+ */
1134
+ getParentNode(node) {
1135
+ const keyPath = node.property.keyPath;
1136
+ if (!keyPath || keyPath.length <= 1) {
1137
+ return null;
1138
+ }
1139
+ const parentPath = keyPath.slice(0, -1).join('.');
1140
+ return this.flatNodeMap.get(parentPath) || null;
1141
+ }
1142
+ /**
1143
+ * Determines if a node should be rendered.
1144
+ * @param node The node to check.
1145
+ * @returns True if the node should be rendered.
1146
+ */
1147
+ shouldRender(node) {
1148
+ // Always render root nodes
1149
+ if (node.level === 0) {
1150
+ return true;
1151
+ }
1152
+ // For non-root nodes, check if parent is expanded
1153
+ let parent = this.getParentNode(node);
1154
+ while (parent) {
1155
+ if (!this.tree()?.isExpanded(parent)) {
1156
+ return false;
1157
+ }
1158
+ parent = this.getParentNode(parent);
1159
+ }
1160
+ return true;
1161
+ }
1162
+ /**
1163
+ * Selects or deselects all nodes.
1164
+ * @param selected True to select all, false to deselect.
1165
+ */
1166
+ selectAll(selected) {
1167
+ this.allSelected = selected;
1168
+ this.indeterminate = false;
1169
+ const visibleNodes = this.dataSource.data.filter(node => node.isVisible);
1170
+ visibleNodes.forEach(node => {
1171
+ node.property.active = selected;
1172
+ });
1173
+ const selectedProperties = this.dataSource.data
1174
+ .filter(n => n.property.active)
1175
+ .map(n => n.property);
1176
+ this.selectedProperties.next(selectedProperties);
1177
+ }
1178
+ /**
1179
+ * Handles single selection mode.
1180
+ * @param selected True if the node is selected.
1181
+ * @param node The node to select.
1182
+ */
1183
+ onSelectSingle(selected, node) {
1184
+ this.dataSource.data.forEach(n => {
1185
+ n.property.active = false;
1186
+ });
1187
+ node.property.active = selected;
1188
+ if (selected && node.expandable && this.tree() && !this.tree().isExpanded(node)) {
1189
+ this.tree().expand(node);
1190
+ }
1191
+ this.selectedProperties.next([node.property]);
1192
+ }
1193
+ /**
1194
+ * Handles multi-selection mode.
1195
+ * @param selected True if the node is selected.
1196
+ * @param node The node to select.
1197
+ */
1198
+ onSelectMulti(selected, node) {
1199
+ node.property.active = selected;
1200
+ if (selected && node.expandable && this.tree() && !this.tree().isExpanded(node)) {
1201
+ this.tree().expand(node);
1202
+ }
1203
+ if (node.expandable) {
1204
+ this.updateChildSelectionStatus(node, selected);
1205
+ }
1206
+ this.updateSelectAllState();
1207
+ const selectedProperties = this.dataSource.data
1208
+ .filter(n => n.property.active)
1209
+ .map(n => n.property);
1210
+ this.selectedProperties.next(selectedProperties);
1211
+ }
1212
+ /**
1213
+ * Initiates a search operation.
1214
+ */
1215
+ onSearch() {
1216
+ this.searchSubject$.next(this.inputText);
1217
+ }
1218
+ /**
1219
+ * Clears the search input.
1220
+ */
1221
+ clearSearch() {
1222
+ this.inputText = '';
1223
+ this.onSearch();
1224
+ }
1225
+ collapseButtonTitle(node) {
1226
+ const expanded = this.tree().isExpanded(node);
1227
+ return expanded
1228
+ ? gettext('Collapse {{ assetPropertyLabel }}')
1229
+ : gettext('Expand {{ assetPropertyLabel }}');
1230
+ }
1231
+ /**
1232
+ * Updates the list of properties based on the configuration.
1233
+ */
1234
+ async updateProperties() {
1235
+ if (this.config.inputPropertiesHandle === 'merge' || !this.config.inputPropertiesHandle) {
1236
+ this.properties = [...this.customProperties, ...(await this.getProperties())];
1237
+ }
1238
+ else if (this.config.inputPropertiesHandle === 'override') {
1239
+ this.properties = [...this.customProperties];
1240
+ }
1241
+ this.buildTreeNodes();
1242
+ }
1243
+ /**
1244
+ * Updates the selection status of child nodes.
1245
+ * @param parent The parent node.
1246
+ * @param selected True if the parent is selected.
1247
+ */
1248
+ updateChildSelectionStatus(parent, selected) {
1249
+ if (!parent.expandable)
1250
+ return;
1251
+ const parentKey = parent.property.keyPath?.join('.') || parent.property.name;
1252
+ this.dataSource.data.forEach(node => {
1253
+ const nodePath = node.property.keyPath;
1254
+ if (nodePath && nodePath.join('.').startsWith(parentKey) && node !== parent) {
1255
+ node.property.active = selected;
1256
+ }
1257
+ });
1258
+ }
1259
+ /**
1260
+ * Checks if a property matches the search criteria.
1261
+ * @param property The property to check.
1262
+ * @param search The search text.
1263
+ * @returns True if the property matches.
1264
+ */
1265
+ matchesSearch(property, search) {
1266
+ const searchableFields = [
1267
+ property.label,
1268
+ property.name,
1269
+ property.keyPath?.join('.'),
1270
+ property.title
1271
+ ].filter(Boolean);
1272
+ return searchableFields.some(field => field?.toLowerCase().includes(search));
1273
+ }
1274
+ /**
1275
+ * Updates the selection state for all nodes.
1276
+ */
1277
+ updateSelectAllState() {
1278
+ if (this.config.selectMode !== 'multi')
1279
+ return;
1280
+ const visibleNodes = this.dataSource.data.filter(node => node.isVisible);
1281
+ const totalCount = visibleNodes.length;
1282
+ const selectedCount = visibleNodes.filter(node => node.property.active).length;
1283
+ this.allSelected = totalCount > 0 && selectedCount === totalCount;
1284
+ this.indeterminate = selectedCount > 0 && selectedCount < totalCount;
1285
+ // Update indeterminate state for each complex node
1286
+ this.updateIndeterminateStates();
1287
+ }
1288
+ /**
1289
+ * Builds the tree nodes from the properties.
1290
+ */
1291
+ buildTreeNodes() {
1292
+ const treeData = [];
1293
+ this.flatNodeMap.clear();
1294
+ this.properties.forEach(property => {
1295
+ const isComplex = this.assetPropertiesService.isComplexProperty(property);
1296
+ const hasKeyPath = !!property.keyPath;
1297
+ const level = hasKeyPath ? property.keyPath.length - 1 : 0;
1298
+ const key = hasKeyPath ? property.keyPath.join('.') : property.name;
1299
+ const node = {
1300
+ expandable: isComplex,
1301
+ level,
1302
+ property,
1303
+ isVisible: true,
1304
+ indeterminate: false // add indeterminate property
1305
+ };
1306
+ this.flatNodeMap.set(key, node);
1307
+ treeData.push(node);
1308
+ });
1309
+ this.dataSource.data = treeData;
1310
+ this.filterTree();
1311
+ this.updateSelectAllState();
1312
+ if (this.config.expansionMode === 'nonCollapsible' ||
1313
+ this.config.expansionMode === 'expandedByDefault') {
1314
+ queueMicrotask(() => {
1315
+ this.expandAllNodes();
1316
+ });
1317
+ }
1318
+ }
1319
+ /**
1320
+ * Update indeterminate state for all complex nodes
1321
+ */
1322
+ updateIndeterminateStates() {
1323
+ // Only for multi-select mode
1324
+ if (this.config.selectMode !== 'multi')
1325
+ return;
1326
+ // For each complex node, check its children
1327
+ this.dataSource.data.forEach(node => {
1328
+ if (node.expandable) {
1329
+ const parentKey = node.property.keyPath?.join('.') || node.property.name;
1330
+ // Find direct children (level = parent.level + 1, keyPath starts with parentKey)
1331
+ const children = this.dataSource.data.filter(child => {
1332
+ if (child === node)
1333
+ return false;
1334
+ const childKeyPath = child.property.keyPath;
1335
+ if (!childKeyPath)
1336
+ return false;
1337
+ // Direct child: one level deeper, and keyPath starts with parentKey
1338
+ return (childKeyPath.length === node.level + 2 &&
1339
+ childKeyPath.slice(0, node.level + 1).join('.') === parentKey);
1340
+ });
1341
+ if (children.length > 0) {
1342
+ const selectedCount = children.filter(child => child.property.active).length;
1343
+ node.indeterminate = selectedCount > 0 && selectedCount < children.length;
1344
+ }
1345
+ else {
1346
+ node.indeterminate = false;
1347
+ }
1348
+ }
1349
+ else {
1350
+ node.indeterminate = false;
1351
+ }
1352
+ });
1353
+ }
1354
+ /**
1355
+ * Expands all nodes in the tree.
1356
+ */
1357
+ expandAllNodes() {
1358
+ if (!this.tree())
1359
+ return;
1360
+ const parentNodes = this.dataSource.data.filter(node => node.expandable);
1361
+ parentNodes.forEach(node => {
1362
+ if (!this.tree().isExpanded(node)) {
1363
+ this.tree().expand(node);
1364
+ }
1365
+ });
1366
+ }
1367
+ /**
1368
+ * Filters the tree nodes based on the search input.
1369
+ */
1370
+ filterTree() {
1371
+ const searchText = this.inputText?.toLowerCase() || '';
1372
+ const allNodes = [...this.dataSource.data];
1373
+ if (!searchText) {
1374
+ allNodes.forEach(node => (node.isVisible = true));
1375
+ this.dataSource.data = Array.from(this.flatNodeMap.values());
1376
+ this.updateSelectAllState();
1377
+ return;
1378
+ }
1379
+ // First pass: Mark nodes that match the search criteria
1380
+ allNodes.forEach(node => {
1381
+ node.isVisible = this.matchesSearch(node.property, searchText);
1382
+ });
1383
+ // Second pass: Make parent nodes of visible nodes also visible
1384
+ allNodes.forEach(node => {
1385
+ if (node.isVisible && node.level > 0) {
1386
+ // Find and mark parent nodes as visible
1387
+ this.makeParentsVisible(node);
1388
+ }
1389
+ });
1390
+ // Final filtered list
1391
+ const filteredNodes = allNodes.filter(node => node.isVisible);
1392
+ // Update the data source with filtered nodes
1393
+ this.dataSource.data = filteredNodes;
1394
+ this.updateSelectAllState();
1395
+ // After data is updated, expand all parent nodes
1396
+ queueMicrotask(() => {
1397
+ this.expandNodesForSearch();
1398
+ });
1399
+ }
1400
+ /**
1401
+ * Expand parent nodes when filtering
1402
+ * This method expands all nodes with children when a search is active
1403
+ */
1404
+ expandNodesForSearch() {
1405
+ if (!this.inputText || !this.tree()) {
1406
+ return;
1407
+ }
1408
+ const parentNodes = this.dataSource.data.filter(node => node.expandable);
1409
+ // Expand all parent nodes when searching
1410
+ parentNodes.forEach(node => {
1411
+ if (!this.tree().isExpanded(node)) {
1412
+ this.tree().expand(node);
1413
+ }
1414
+ });
1415
+ }
1416
+ /**
1417
+ * Makes all parent nodes visible.
1418
+ * @param node The node whose parents should be made visible.
1419
+ */
1420
+ makeParentsVisible(node) {
1421
+ if (!node || node.level === 0)
1422
+ return;
1423
+ const keyPath = node.property.keyPath;
1424
+ if (!keyPath)
1425
+ return;
1426
+ // Navigate up the path
1427
+ for (let i = 1; i < keyPath.length; i++) {
1428
+ const parentPath = keyPath.slice(0, i).join('.');
1429
+ const parent = this.flatNodeMap.get(parentPath);
1430
+ if (parent) {
1431
+ parent.isVisible = true;
1432
+ }
1433
+ else {
1434
+ break;
1435
+ }
1436
+ }
1437
+ }
1438
+ /**
1439
+ * Categorizes and flattens hierarchical properties.
1440
+ * @param properties The properties to categorize.
1441
+ * @returns The flattened list of properties.
1442
+ */
1443
+ categorizeAndFlattenHierarchicalProperties(properties) {
1444
+ const { simple, complex } = this.assetPropertiesService.categorizeAndFlattenHierarchicalProperties(properties);
1445
+ return [...simple, ...complex];
1446
+ }
1447
+ /**
1448
+ * Fix syntax error in the file.
1449
+ */
1450
+ fixSyntaxError() {
1451
+ if (true) {
1452
+ // Correct syntax
1453
+ }
1454
+ }
1455
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertyListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1456
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: AssetPropertyListComponent, isStandalone: true, selector: "c8y-asset-property-list", inputs: { config: "config", asset: "asset", customProperties: "customProperties" }, outputs: { selectedProperties: "selectedProperties" }, queries: [{ propertyName: "assetPropertyAction", first: true, predicate: AssetPropertyActionDirective, descendants: true }], viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"tree-container bg-inherit\"\n [attr.aria-label]=\"'Asset property list' | translate\"\n role=\"tree\"\n>\n <div\n class=\"select-all-container bg-inherit sticky-top separator-bottom\"\n *ngIf=\"config.showHeader || config.searchable\"\n >\n <div\n class=\"form-group m-b-0 p-16\"\n *ngIf=\"config.searchable\"\n [ngClass]=\"{ 'separator-bottom': config.showHeader }\"\n >\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Filter properties' | translate\"\n placeholder=\"{{ 'Filter properties' | translate }}\"\n role=\"searchbox\"\n type=\"search\"\n autocomplete=\"off\"\n [(ngModel)]=\"inputText\"\n (input)=\"onSearch()\"\n [disabled]=\"!dataSource.data.length\"\n #filter\n />\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n *ngIf=\"filter.value.length === 0\"\n >\n <i\n class=\"dlt-c8y-icon-search\"\n [c8yIcon]=\"'search'\"\n ></i>\n </span>\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n *ngIf=\"filter.value.length > 0\"\n >\n <i\n class=\"text-muted dlt-c8y-icon-times\"\n [c8yIcon]=\"'times'\"\n [attr.aria-label]=\"'Clear search' | translate\"\n tabindex=\"0\"\n role=\"button\"\n (click)=\"clearSearch()\"\n ></i>\n </span>\n </div>\n </div>\n <div\n class=\"d-flex a-i-center overflow-hidden\"\n *ngIf=\"config.showHeader && dataSource.data.length\"\n >\n <div\n class=\"flex-no-shrink\"\n [ngClass]=\"{\n 'p-r-40': config.selectMode !== 'none' && config.expansionMode !== 'nonCollapsible',\n 'p-r-16': config.selectMode === 'none',\n 'p-r-8': config.selectMode !== 'none' && config.expansionMode === 'nonCollapsible'\n }\"\n ></div>\n <c8y-list-item-checkbox\n class=\"p-l-4 p-r-4\"\n *ngIf=\"config.selectMode === 'multi'\"\n [ngModel]=\"allSelected\"\n [indeterminate]=\"indeterminate\"\n (onSelect)=\"selectAll($event)\"\n ></c8y-list-item-checkbox>\n <div\n class=\"p-l-24 p-t-40\"\n *ngIf=\"config.selectMode !== 'multi'\"\n ></div>\n <div class=\"content-flex-30 fit-w m-t-4 m-b-4\">\n <div class=\"col-6 d-flex a-i-center m-l-0 m-r-0\">\n <span class=\"p-r-4 p-l-16 flex-no-shrink\">\n <div class=\"c8y-icon\"></div>\n </span>\n <span class=\"text-medium m-l-8\">\n {{ 'Property' | translate }}\n </span>\n </div>\n <div\n [ngClass]=\"{ 'col-3': config.showValue, 'col-6': !config.showValue }\"\n *ngIf=\"config.showKey\"\n >\n <span class=\"text-medium\">{{ 'Key' | translate }}</span>\n </div>\n <div\n [ngClass]=\"{ 'col-3': config.showKey, 'col-6': !config.showKey }\"\n *ngIf=\"config.showValue\"\n >\n <span class=\"text-medium\">{{ 'Value' | translate }}</span>\n </div>\n </div>\n <div\n class=\"m-l-8 p-l-24\"\n *ngIf=\"assetPropertyAction\"\n ></div>\n </div>\n </div>\n\n <cdk-tree\n role=\"presentation\"\n *ngIf=\"dataSource.data.length; else emptyState\"\n #tree\n [dataSource]=\"dataSource\"\n [levelAccessor]=\"getLevel\"\n >\n <!-- Tree Node Definition -->\n <cdk-tree-node\n [style.display]=\"shouldRender(node) ? 'flex' : 'none'\"\n [attr.tabindex]=\"0\"\n [attr.aria-level]=\"getLevel(node) + 1\"\n [attr.aria-expanded]=\"hasChild(node) ? tree.isExpanded(node) : null\"\n [attr.aria-selected]=\"config.selectMode !== 'none' ? node.property.active : null\"\n role=\"treeitem\"\n *cdkTreeNodeDef=\"let node\"\n cdkTreeNodePadding\n [cdkTreeNodePaddingIndent]=\"24\"\n [ngClass]=\"{\n nonCollapsible: config.expansionMode === 'nonCollapsible',\n nonSelectable: config.selectMode === 'none'\n }\"\n >\n <div class=\"d-flex p-relative overflow-visible bg-inherit fit-h\">\n <!-- Toggle Button for expandable nodes -->\n <button\n class=\"collapse-btn btn-dot flex-no-shrink\"\n [attr.aria-label]=\"\n collapseButtonTitle(node)\n | translate\n : {\n assetPropertyLabel:\n node.property.label || node.property.title || node.property.name\n }\n \"\n [attr.aria-expanded]=\"tree.isExpanded(node)\"\n *ngIf=\"config.expansionMode !== 'nonCollapsible' && hasChild(node)\"\n cdkTreeNodeToggle\n >\n <i [c8yIcon]=\"'chevron-right'\"></i>\n </button>\n <!-- Placeholder for non-expandable nodes to maintain alignment -->\n <div\n class=\"flex-no-shrink\"\n [ngClass]=\"{\n 'p-r-40': config.expansionMode !== 'nonCollapsible',\n 'p-r-8': config.expansionMode === 'nonCollapsible'\n }\"\n *ngIf=\"!hasChild(node) || config.expansionMode === 'nonCollapsible'\"\n ></div>\n\n <!-- Selection Controls -->\n <div\n class=\"d-contents\"\n *ngIf=\"config.selectMode !== 'none'\"\n >\n @if (config.selectMode === 'single') {\n <c8y-list-item-radio\n class=\"p-l-4\"\n type=\"radio\"\n [ngModel]=\"node.property.active\"\n (onSelect)=\"onSelectSingle($event, node)\"\n ></c8y-list-item-radio>\n } @else if (config.selectMode === 'multi') {\n <c8y-list-item-checkbox\n class=\"p-l-4\"\n [ngModel]=\"node.property.active\"\n [indeterminate]=\"node.indeterminate\"\n (onSelect)=\"onSelectMulti($event, node)\"\n ></c8y-list-item-checkbox>\n }\n </div>\n\n <div class=\"content-flex-30 fit-w bg-inherit\">\n <div\n class=\"d-flex a-i-center bg-inherit m-0\"\n [ngClass]=\"{\n 'col-6': config.showKey || config.showValue,\n 'col-12': !config.showKey && !config.showValue\n }\"\n >\n <c8y-li-icon\n class=\"p-r-4\"\n [icon]=\"node.property.type | c8yAssetPropertyIcon\"\n tooltip=\"{{ node.property.type }}\"\n [delay]=\"500\"\n [ngClass]=\"{\n 'p-l-4': config.selectMode !== 'none',\n 'p-r-16': config.selectMode === 'none'\n }\"\n ></c8y-li-icon>\n\n <span class=\"p-r-8\">\n <div\n class=\"text-truncate\"\n title=\"{{\n node.property.label || node.property.title || node.property.name | translate\n }}\"\n >\n {{ node.property.label || node.property.title || node.property.name | translate }}\n </div>\n </span>\n </div>\n <div\n class=\"d-flex a-i-center\"\n *ngIf=\"config.showKey\"\n [ngClass]=\"{ 'col-3': config.showValue, 'col-6': !config.showValue }\"\n >\n <span\n class=\"d-inline-block tag tag--default a-s-center text-truncate\"\n title=\"{{ node.property.keyPath?.join('.') || (node.property?.name | translate) }}\"\n >\n {{ node.property.keyPath?.join('.') || (node.property?.name | translate) }}\n </span>\n </div>\n <div\n class=\"d-flex a-i-center\"\n [ngClass]=\"{ 'col-3': config.showKey, 'col-6': !config.showKey }\"\n *ngIf=\"asset && config.showValue\"\n >\n <span\n class=\"tag tag--info d-inline-block a-s-center text-truncate\"\n title=\"{{ node.property | c8yAssetPropertyValue: asset }}\"\n >\n {{ node.property | c8yAssetPropertyValue: asset }}\n </span>\n </div>\n </div>\n <div\n class=\"m-l-8 showOnHover d-flex a-i-center\"\n *ngIf=\"assetPropertyAction\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n assetPropertyAction?.template;\n context: {\n $implicit: node.property\n }\n \"\n ></ng-container>\n </div>\n </div>\n </cdk-tree-node>\n </cdk-tree>\n</div>\n\n<ng-template #emptyState>\n <c8y-ui-empty-state\n icon=\"list\"\n title=\"{{ 'No properties to display' | translate }}\"\n subtitle=\"{{ 'Select an asset to see the available properties.' | translate }}\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n</ng-template>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ListGroupModule }, { kind: "component", type: i2.ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "component", type: i2.ListItemCheckboxComponent, selector: "c8y-list-item-checkbox, c8y-li-checkbox", inputs: ["selected", "indeterminate", "disabled", "displayAsSwitch"], outputs: ["onSelect"] }, { kind: "component", type: i2.ListItemRadioComponent, selector: "c8y-list-item-radio, c8y-li-radio", inputs: ["selected", "name", "disabled", "value"], outputs: ["onSelect"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: CdkTreeModule }, { kind: "directive", type: i4.CdkTreeNodeDef, selector: "[cdkTreeNodeDef]", inputs: ["cdkTreeNodeDefWhen"] }, { kind: "directive", type: i4.CdkTreeNodePadding, selector: "[cdkTreeNodePadding]", inputs: ["cdkTreeNodePadding", "cdkTreeNodePaddingIndent"] }, { kind: "directive", type: i4.CdkTreeNodeToggle, selector: "[cdkTreeNodeToggle]", inputs: ["cdkTreeNodeToggleRecursive"] }, { kind: "component", type: i4.CdkTree, selector: "cdk-tree", inputs: ["dataSource", "treeControl", "levelAccessor", "childrenAccessor", "trackBy", "expansionKey"], exportAs: ["cdkTree"] }, { kind: "directive", type: i4.CdkTreeNode, selector: "cdk-tree-node", inputs: ["role", "isExpandable", "isExpanded", "isDisabled", "cdkTreeNodeTypeaheadLabel"], outputs: ["activation", "expandedChange"], exportAs: ["cdkTreeNode"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: AssetPropertyValuePipe, name: "c8yAssetPropertyValue" }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "pipe", type: AssetPropertyIconPipe, name: "c8yAssetPropertyIcon" }] }); }
1457
+ }
1458
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertyListComponent, decorators: [{
1459
+ type: Component,
1460
+ args: [{ selector: 'c8y-asset-property-list', standalone: true, imports: [
1461
+ NgIf,
1462
+ NgClass,
1463
+ FormsModule,
1464
+ ListGroupModule,
1465
+ C8yTranslatePipe,
1466
+ TooltipModule,
1467
+ IconDirective,
1468
+ CdkTreeModule,
1469
+ NgTemplateOutlet,
1470
+ AssetPropertyValuePipe,
1471
+ EmptyStateComponent,
1472
+ AssetPropertyIconPipe
1473
+ ], template: "<div\n class=\"tree-container bg-inherit\"\n [attr.aria-label]=\"'Asset property list' | translate\"\n role=\"tree\"\n>\n <div\n class=\"select-all-container bg-inherit sticky-top separator-bottom\"\n *ngIf=\"config.showHeader || config.searchable\"\n >\n <div\n class=\"form-group m-b-0 p-16\"\n *ngIf=\"config.searchable\"\n [ngClass]=\"{ 'separator-bottom': config.showHeader }\"\n >\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Filter properties' | translate\"\n placeholder=\"{{ 'Filter properties' | translate }}\"\n role=\"searchbox\"\n type=\"search\"\n autocomplete=\"off\"\n [(ngModel)]=\"inputText\"\n (input)=\"onSearch()\"\n [disabled]=\"!dataSource.data.length\"\n #filter\n />\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n *ngIf=\"filter.value.length === 0\"\n >\n <i\n class=\"dlt-c8y-icon-search\"\n [c8yIcon]=\"'search'\"\n ></i>\n </span>\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n *ngIf=\"filter.value.length > 0\"\n >\n <i\n class=\"text-muted dlt-c8y-icon-times\"\n [c8yIcon]=\"'times'\"\n [attr.aria-label]=\"'Clear search' | translate\"\n tabindex=\"0\"\n role=\"button\"\n (click)=\"clearSearch()\"\n ></i>\n </span>\n </div>\n </div>\n <div\n class=\"d-flex a-i-center overflow-hidden\"\n *ngIf=\"config.showHeader && dataSource.data.length\"\n >\n <div\n class=\"flex-no-shrink\"\n [ngClass]=\"{\n 'p-r-40': config.selectMode !== 'none' && config.expansionMode !== 'nonCollapsible',\n 'p-r-16': config.selectMode === 'none',\n 'p-r-8': config.selectMode !== 'none' && config.expansionMode === 'nonCollapsible'\n }\"\n ></div>\n <c8y-list-item-checkbox\n class=\"p-l-4 p-r-4\"\n *ngIf=\"config.selectMode === 'multi'\"\n [ngModel]=\"allSelected\"\n [indeterminate]=\"indeterminate\"\n (onSelect)=\"selectAll($event)\"\n ></c8y-list-item-checkbox>\n <div\n class=\"p-l-24 p-t-40\"\n *ngIf=\"config.selectMode !== 'multi'\"\n ></div>\n <div class=\"content-flex-30 fit-w m-t-4 m-b-4\">\n <div class=\"col-6 d-flex a-i-center m-l-0 m-r-0\">\n <span class=\"p-r-4 p-l-16 flex-no-shrink\">\n <div class=\"c8y-icon\"></div>\n </span>\n <span class=\"text-medium m-l-8\">\n {{ 'Property' | translate }}\n </span>\n </div>\n <div\n [ngClass]=\"{ 'col-3': config.showValue, 'col-6': !config.showValue }\"\n *ngIf=\"config.showKey\"\n >\n <span class=\"text-medium\">{{ 'Key' | translate }}</span>\n </div>\n <div\n [ngClass]=\"{ 'col-3': config.showKey, 'col-6': !config.showKey }\"\n *ngIf=\"config.showValue\"\n >\n <span class=\"text-medium\">{{ 'Value' | translate }}</span>\n </div>\n </div>\n <div\n class=\"m-l-8 p-l-24\"\n *ngIf=\"assetPropertyAction\"\n ></div>\n </div>\n </div>\n\n <cdk-tree\n role=\"presentation\"\n *ngIf=\"dataSource.data.length; else emptyState\"\n #tree\n [dataSource]=\"dataSource\"\n [levelAccessor]=\"getLevel\"\n >\n <!-- Tree Node Definition -->\n <cdk-tree-node\n [style.display]=\"shouldRender(node) ? 'flex' : 'none'\"\n [attr.tabindex]=\"0\"\n [attr.aria-level]=\"getLevel(node) + 1\"\n [attr.aria-expanded]=\"hasChild(node) ? tree.isExpanded(node) : null\"\n [attr.aria-selected]=\"config.selectMode !== 'none' ? node.property.active : null\"\n role=\"treeitem\"\n *cdkTreeNodeDef=\"let node\"\n cdkTreeNodePadding\n [cdkTreeNodePaddingIndent]=\"24\"\n [ngClass]=\"{\n nonCollapsible: config.expansionMode === 'nonCollapsible',\n nonSelectable: config.selectMode === 'none'\n }\"\n >\n <div class=\"d-flex p-relative overflow-visible bg-inherit fit-h\">\n <!-- Toggle Button for expandable nodes -->\n <button\n class=\"collapse-btn btn-dot flex-no-shrink\"\n [attr.aria-label]=\"\n collapseButtonTitle(node)\n | translate\n : {\n assetPropertyLabel:\n node.property.label || node.property.title || node.property.name\n }\n \"\n [attr.aria-expanded]=\"tree.isExpanded(node)\"\n *ngIf=\"config.expansionMode !== 'nonCollapsible' && hasChild(node)\"\n cdkTreeNodeToggle\n >\n <i [c8yIcon]=\"'chevron-right'\"></i>\n </button>\n <!-- Placeholder for non-expandable nodes to maintain alignment -->\n <div\n class=\"flex-no-shrink\"\n [ngClass]=\"{\n 'p-r-40': config.expansionMode !== 'nonCollapsible',\n 'p-r-8': config.expansionMode === 'nonCollapsible'\n }\"\n *ngIf=\"!hasChild(node) || config.expansionMode === 'nonCollapsible'\"\n ></div>\n\n <!-- Selection Controls -->\n <div\n class=\"d-contents\"\n *ngIf=\"config.selectMode !== 'none'\"\n >\n @if (config.selectMode === 'single') {\n <c8y-list-item-radio\n class=\"p-l-4\"\n type=\"radio\"\n [ngModel]=\"node.property.active\"\n (onSelect)=\"onSelectSingle($event, node)\"\n ></c8y-list-item-radio>\n } @else if (config.selectMode === 'multi') {\n <c8y-list-item-checkbox\n class=\"p-l-4\"\n [ngModel]=\"node.property.active\"\n [indeterminate]=\"node.indeterminate\"\n (onSelect)=\"onSelectMulti($event, node)\"\n ></c8y-list-item-checkbox>\n }\n </div>\n\n <div class=\"content-flex-30 fit-w bg-inherit\">\n <div\n class=\"d-flex a-i-center bg-inherit m-0\"\n [ngClass]=\"{\n 'col-6': config.showKey || config.showValue,\n 'col-12': !config.showKey && !config.showValue\n }\"\n >\n <c8y-li-icon\n class=\"p-r-4\"\n [icon]=\"node.property.type | c8yAssetPropertyIcon\"\n tooltip=\"{{ node.property.type }}\"\n [delay]=\"500\"\n [ngClass]=\"{\n 'p-l-4': config.selectMode !== 'none',\n 'p-r-16': config.selectMode === 'none'\n }\"\n ></c8y-li-icon>\n\n <span class=\"p-r-8\">\n <div\n class=\"text-truncate\"\n title=\"{{\n node.property.label || node.property.title || node.property.name | translate\n }}\"\n >\n {{ node.property.label || node.property.title || node.property.name | translate }}\n </div>\n </span>\n </div>\n <div\n class=\"d-flex a-i-center\"\n *ngIf=\"config.showKey\"\n [ngClass]=\"{ 'col-3': config.showValue, 'col-6': !config.showValue }\"\n >\n <span\n class=\"d-inline-block tag tag--default a-s-center text-truncate\"\n title=\"{{ node.property.keyPath?.join('.') || (node.property?.name | translate) }}\"\n >\n {{ node.property.keyPath?.join('.') || (node.property?.name | translate) }}\n </span>\n </div>\n <div\n class=\"d-flex a-i-center\"\n [ngClass]=\"{ 'col-3': config.showKey, 'col-6': !config.showKey }\"\n *ngIf=\"asset && config.showValue\"\n >\n <span\n class=\"tag tag--info d-inline-block a-s-center text-truncate\"\n title=\"{{ node.property | c8yAssetPropertyValue: asset }}\"\n >\n {{ node.property | c8yAssetPropertyValue: asset }}\n </span>\n </div>\n </div>\n <div\n class=\"m-l-8 showOnHover d-flex a-i-center\"\n *ngIf=\"assetPropertyAction\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n assetPropertyAction?.template;\n context: {\n $implicit: node.property\n }\n \"\n ></ng-container>\n </div>\n </div>\n </cdk-tree-node>\n </cdk-tree>\n</div>\n\n<ng-template #emptyState>\n <c8y-ui-empty-state\n icon=\"list\"\n title=\"{{ 'No properties to display' | translate }}\"\n subtitle=\"{{ 'Select an asset to see the available properties.' | translate }}\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n</ng-template>\n" }]
1474
+ }], ctorParameters: () => [], propDecorators: { config: [{
1475
+ type: Input
1476
+ }], asset: [{
1477
+ type: Input
1478
+ }], customProperties: [{
1479
+ type: Input
1480
+ }], selectedProperties: [{
1481
+ type: Output
1482
+ }], assetPropertyAction: [{
1483
+ type: ContentChild,
1484
+ args: [AssetPropertyActionDirective]
1485
+ }] } });
1486
+
1487
+ /**
1488
+ * Represents a drawer component for selecting asset properties.
1489
+ */
1490
+ class AssetPropertySelectorDrawerComponent {
1491
+ constructor() {
1492
+ /**
1493
+ * Title of the drawer.
1494
+ */
1495
+ this.title = gettext('Select property');
1496
+ /**
1497
+ * Custom properties to be displayed in the list.
1498
+ */
1499
+ this.customProperties = [];
1500
+ /**
1501
+ * Emits the selected properties when saved.
1502
+ */
1503
+ this.savePropertySelection = new EventEmitter();
1504
+ /**
1505
+ * Emits an event when the selection is canceled.
1506
+ */
1507
+ this.cancelPropertySelection = new EventEmitter();
1508
+ /**
1509
+ * List of selected properties.
1510
+ */
1511
+ this.selectedProperties = [];
1512
+ /**
1513
+ * Reference to the bottom drawer.
1514
+ */
1515
+ this.bottomDrawerRef = inject(BottomDrawerRef);
1516
+ /**
1517
+ * Promise resolving to the selected properties.
1518
+ */
1519
+ this.result = new Promise((resolve, reject) => {
1520
+ this._save = resolve;
1521
+ this._cancel = reject;
1522
+ });
1523
+ }
1524
+ /**
1525
+ * Updates the selected properties.
1526
+ * @param properties The selected properties.
1527
+ */
1528
+ onSelectedProperties(properties) {
1529
+ this.selectedProperties = cloneDeep(properties);
1530
+ }
1531
+ /**
1532
+ * Saves the selected properties and closes the drawer.
1533
+ */
1534
+ onSave() {
1535
+ this._save(this.selectedProperties);
1536
+ this.bottomDrawerRef.close();
1537
+ }
1538
+ /**
1539
+ * Cancels the selection and closes the drawer.
1540
+ */
1541
+ onCancel() {
1542
+ this._cancel();
1543
+ this.bottomDrawerRef.close();
1544
+ }
1545
+ /**
1546
+ * Checks if the select button should be disabled.
1547
+ * @returns True if all selected properties are inactive.
1548
+ */
1549
+ selectIsDisabled() {
1550
+ return this.selectedProperties?.every(({ active }) => !active);
1551
+ }
1552
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertySelectorDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1553
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AssetPropertySelectorDrawerComponent, isStandalone: true, selector: "c8y-asset-property-selector-drawer-component", inputs: { title: "title" }, outputs: { savePropertySelection: "savePropertySelection", cancelPropertySelection: "cancelPropertySelection" }, host: { classAttribute: "d-contents" }, ngImport: i0, template: "<div class=\"card-header separator\">\n <span class=\"h4 card-title\">{{ title | translate }}</span>\n</div>\n\n<div class=\"inner-scroll flex-grow\">\n <c8y-asset-property-list\n class=\"bg-component\"\n [asset]=\"asset\"\n [config]=\"config\"\n [customProperties]=\"customProperties\"\n (selectedProperties)=\"onSelectedProperties($event)\"\n ></c8y-asset-property-list>\n</div>\n\n<div class=\"card-footer text-center p-24 separator flex-no-shrink\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"onCancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Select' | translate }}\"\n type=\"button\"\n [disabled]=\"selectIsDisabled()\"\n (click)=\"onSave()\"\n >\n {{ 'Select' | translate }}\n </button>\n</div>\n", dependencies: [{ kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "ngmodule", type: FormsModule$1 }, { kind: "component", type: AssetPropertyListComponent, selector: "c8y-asset-property-list", inputs: ["config", "asset", "customProperties"], outputs: ["selectedProperties"] }] }); }
1554
+ }
1555
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AssetPropertySelectorDrawerComponent, decorators: [{
1556
+ type: Component,
1557
+ args: [{ selector: 'c8y-asset-property-selector-drawer-component', host: {
1558
+ class: 'd-contents'
1559
+ }, standalone: true, imports: [C8yTranslatePipe, FormsModule$1, AssetPropertyListComponent], template: "<div class=\"card-header separator\">\n <span class=\"h4 card-title\">{{ title | translate }}</span>\n</div>\n\n<div class=\"inner-scroll flex-grow\">\n <c8y-asset-property-list\n class=\"bg-component\"\n [asset]=\"asset\"\n [config]=\"config\"\n [customProperties]=\"customProperties\"\n (selectedProperties)=\"onSelectedProperties($event)\"\n ></c8y-asset-property-list>\n</div>\n\n<div class=\"card-footer text-center p-24 separator flex-no-shrink\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"onCancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Select' | translate }}\"\n type=\"button\"\n [disabled]=\"selectIsDisabled()\"\n (click)=\"onSave()\"\n >\n {{ 'Select' | translate }}\n </button>\n</div>\n" }]
1560
+ }], propDecorators: { title: [{
1561
+ type: Input
1562
+ }], savePropertySelection: [{
1563
+ type: Output
1564
+ }], cancelPropertySelection: [{
1565
+ type: Output
1566
+ }] } });
1567
+
1568
+ /**
1569
+ * Generated bundle index. Do not edit.
1570
+ */
1571
+
1572
+ export { AssetPropertiesService, AssetPropertyActionDirective, AssetPropertyListComponent, AssetPropertySelectorDrawerComponent, RESULT_TYPES, defaultAssetProperties, defaultAssetPropertyListConfig, deviceAssetProperties };
1573
+ //# sourceMappingURL=c8y-ngx-components-asset-properties.mjs.map