@syncfusion/ej2-lists 20.1.55 → 20.2.36

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 (59) hide show
  1. package/CHANGELOG.md +0 -2
  2. package/dist/ej2-lists.umd.min.js +1 -1
  3. package/dist/global/ej2-lists.min.js +1 -1
  4. package/dist/global/index.d.ts +1 -1
  5. package/dist/ts/common/list-base.ts +1283 -0
  6. package/dist/ts/component.ts +4 -0
  7. package/dist/ts/list-view/list-view.ts +2595 -0
  8. package/dist/ts/list-view/virtualization.ts +1155 -0
  9. package/dist/ts/sortable/sortable.ts +458 -0
  10. package/package.json +8 -8
  11. package/styles/bootstrap-dark.css +7 -59
  12. package/styles/bootstrap.css +7 -58
  13. package/styles/bootstrap4.css +7 -66
  14. package/styles/bootstrap5-dark.css +7 -63
  15. package/styles/bootstrap5.css +7 -63
  16. package/styles/fabric-dark.css +7 -59
  17. package/styles/fabric.css +7 -58
  18. package/styles/fluent-dark.css +7 -68
  19. package/styles/fluent.css +7 -68
  20. package/styles/highcontrast-light.css +7 -59
  21. package/styles/highcontrast.css +7 -58
  22. package/styles/list-view/_fusionnew-definition.scss +129 -0
  23. package/styles/list-view/_material3-definition.scss +129 -0
  24. package/styles/list-view/bootstrap-dark.css +6 -55
  25. package/styles/list-view/bootstrap.css +6 -54
  26. package/styles/list-view/bootstrap4.css +6 -62
  27. package/styles/list-view/bootstrap5-dark.css +6 -59
  28. package/styles/list-view/bootstrap5.css +6 -59
  29. package/styles/list-view/fabric-dark.css +6 -55
  30. package/styles/list-view/fabric.css +6 -54
  31. package/styles/list-view/fluent-dark.css +6 -64
  32. package/styles/list-view/fluent.css +6 -64
  33. package/styles/list-view/highcontrast-light.css +6 -55
  34. package/styles/list-view/highcontrast.css +6 -54
  35. package/styles/list-view/icons/_fusionnew.scss +8 -0
  36. package/styles/list-view/icons/_material3.scss +8 -0
  37. package/styles/list-view/material-dark.css +6 -55
  38. package/styles/list-view/material.css +6 -54
  39. package/styles/list-view/tailwind-dark.css +5 -81
  40. package/styles/list-view/tailwind.css +5 -81
  41. package/styles/material-dark.css +7 -59
  42. package/styles/material.css +7 -58
  43. package/styles/sortable/bootstrap-dark.css +2 -5
  44. package/styles/sortable/bootstrap.css +2 -5
  45. package/styles/sortable/bootstrap4.css +2 -5
  46. package/styles/sortable/bootstrap5-dark.css +2 -5
  47. package/styles/sortable/bootstrap5.css +2 -5
  48. package/styles/sortable/fabric-dark.css +2 -5
  49. package/styles/sortable/fabric.css +2 -5
  50. package/styles/sortable/fluent-dark.css +2 -5
  51. package/styles/sortable/fluent.css +2 -5
  52. package/styles/sortable/highcontrast-light.css +2 -5
  53. package/styles/sortable/highcontrast.css +2 -5
  54. package/styles/sortable/material-dark.css +2 -5
  55. package/styles/sortable/material.css +2 -5
  56. package/styles/sortable/tailwind-dark.css +2 -5
  57. package/styles/sortable/tailwind.css +2 -5
  58. package/styles/tailwind-dark.css +6 -85
  59. package/styles/tailwind.css +6 -85
@@ -0,0 +1,2595 @@
1
+ import { Virtualization } from './virtualization';
2
+ import { merge, formatUnit, isNullOrUndefined, append, detach, ModuleDeclaration, extend } from '@syncfusion/ej2-base';
3
+ import { attributes, addClass, removeClass, prepend, closest, remove } from '@syncfusion/ej2-base';
4
+ import { Component, EventHandler, BaseEventArgs, Property, Complex, Event } from '@syncfusion/ej2-base';
5
+ import { NotifyPropertyChanges, INotifyPropertyChanged, ChildProperty } from '@syncfusion/ej2-base';
6
+ import { KeyboardEventArgs, EmitType, compile, SanitizeHtmlHelper } from '@syncfusion/ej2-base';
7
+ import { Animation, AnimationOptions, Effect, rippleEffect, Touch, SwipeEventArgs } from '@syncfusion/ej2-base';
8
+ import { DataManager, Query } from '@syncfusion/ej2-data';
9
+ import { createCheckBox } from '@syncfusion/ej2-buttons';
10
+ import { ListBase, ListBaseOptions, SortOrder, getFieldValues, FieldsMapping } from '../common/list-base';
11
+ import { ListViewModel, FieldSettingsModel } from './list-view-model';
12
+
13
+ // Effect Configuration Effect[] = [fromViewBackward,fromViewForward,toViewBackward,toviewForward];
14
+ const effectsConfig: { [key: string]: Effect[] } = {
15
+ 'None': [],
16
+ 'SlideLeft': ['SlideRightOut', 'SlideLeftOut', 'SlideLeftIn', 'SlideRightIn'],
17
+ 'SlideDown': ['SlideTopOut', 'SlideBottomOut', 'SlideBottomIn', 'SlideTopIn'],
18
+ 'Zoom': ['FadeOut', 'FadeZoomOut', 'FadeZoomIn', 'FadeIn'],
19
+ 'Fade': ['FadeOut', 'FadeOut', 'FadeIn', 'FadeIn']
20
+ };
21
+
22
+ const effectsRTLConfig: { [key: string]: Effect[] } = {
23
+ 'None': [],
24
+ 'SlideLeft': ['SlideLeftOut', 'SlideRightOut', 'SlideRightIn', 'SlideLeftIn'],
25
+ 'SlideDown': ['SlideBottomOut', 'SlideTopOut', 'SlideTopIn', 'SlideBottomIn'],
26
+ 'Zoom': ['FadeZoomOut', 'FadeOut', 'FadeIn', 'FadeZoomIn'],
27
+ 'Fade': ['FadeOut', 'FadeOut', 'FadeIn', 'FadeIn']
28
+ };
29
+
30
+ // don't use space in classnames.
31
+ export const classNames: ClassNames = {
32
+ root: 'e-listview',
33
+ hover: 'e-hover',
34
+ selected: 'e-active',
35
+ focused: 'e-focused',
36
+ parentItem: 'e-list-parent',
37
+ listItem: 'e-list-item',
38
+ listIcon: 'e-list-icon',
39
+ textContent: 'e-text-content',
40
+ listItemText: 'e-list-text',
41
+ groupListItem: 'e-list-group-item',
42
+ hasChild: 'e-has-child',
43
+ view: 'e-view',
44
+ header: 'e-list-header',
45
+ headerText: 'e-headertext',
46
+ headerTemplateText: 'e-headertemplate-text',
47
+ text: 'e-text',
48
+ disable: 'e-disabled',
49
+ content: 'e-content',
50
+ icon: 'e-icons',
51
+ backIcon: 'e-icon-back',
52
+ backButton: 'e-back-button',
53
+ checkboxWrapper: 'e-checkbox-wrapper',
54
+ checkbox: 'e-checkbox',
55
+ checked: 'e-check',
56
+ checklist: 'e-checklist',
57
+ checkboxIcon: 'e-frame',
58
+ checkboxRight: 'e-checkbox-right',
59
+ checkboxLeft: 'e-checkbox-left',
60
+ listviewCheckbox: 'e-listview-checkbox',
61
+ itemCheckList: 'e-checklist',
62
+ virtualElementContainer: 'e-list-virtualcontainer'
63
+ };
64
+
65
+ const LISTVIEW_TEMPLATE_PROPERTY: string = 'Template';
66
+ const LISTVIEW_GROUPTEMPLATE_PROPERTY: string = 'GroupTemplate';
67
+ const LISTVIEW_HEADERTEMPLATE_PROPERTY: string = 'HeaderTemplate';
68
+ const swipeVelocity: number = 0.5;
69
+
70
+ /**
71
+ * An interface that holds options of fields.
72
+ */
73
+ export interface Fields {
74
+ /**
75
+ * Specifies the id field mapped in dataSource.
76
+ */
77
+ id?: string | number;
78
+ /**
79
+ * The `text` property is used to map the text value from the data source for each list item.
80
+ */
81
+ text?: string | number;
82
+ /**
83
+ * It is used to map the custom field values of list item from the dataSource.
84
+ */
85
+ [key: string]: Object | string | number | undefined;
86
+ }
87
+
88
+
89
+ /**
90
+ * Represents the field settings of the ListView.
91
+ */
92
+
93
+ export class FieldSettings extends ChildProperty<FieldSettings> {
94
+
95
+ /**
96
+ * Specifies the id field mapped in dataSource.
97
+ */
98
+ @Property('id')
99
+ public id: string;
100
+ /**
101
+ * The `text` property is used to map the text value from the data source for each list item.
102
+ */
103
+ @Property('text')
104
+ public text: string;
105
+
106
+ /**
107
+ * The `isChecked` property is used to check whether the list items are in checked state or not.
108
+ */
109
+ @Property('isChecked')
110
+ public isChecked: string;
111
+ /**
112
+ * The `isVisible` property is used to check whether the list items are in visible state or not.
113
+ */
114
+ @Property('isVisible')
115
+ public isVisible: string;
116
+ /**
117
+ * Specifies the enabled state of the ListView component.
118
+ * And, we can disable the component using this property by setting its value as false.
119
+ */
120
+ @Property('enabled')
121
+ public enabled: string;
122
+ /**
123
+ * The `iconCss` is used to customize the icon to the list items dynamically.
124
+ * We can add a specific image to the icons using `iconCss` property.
125
+ */
126
+ @Property('iconCss')
127
+ public iconCss: string;
128
+ /**
129
+ * The `child` property is used for nested navigation of listed items.
130
+ */
131
+ @Property('child')
132
+ public child: string;
133
+ /**
134
+ * The `tooltip` is used to display the information about the target element while hovering on list items.
135
+ */
136
+ @Property('tooltip')
137
+ public tooltip: string;
138
+
139
+ /**
140
+ * The `groupBy` property is used to wraps the ListView elements into a group.
141
+ */
142
+ @Property('groupBy')
143
+ public groupBy: string;
144
+
145
+ /**
146
+ * The `sortBy` property used to enable the sorting of list items to be ascending or descending order.
147
+ */
148
+ @Property('text')
149
+ public sortBy: string;
150
+
151
+ /**
152
+ * The `htmlAttributes` allows additional attributes such as id, class, etc., and
153
+ * accepts n number of attributes in a key-value pair format.
154
+ */
155
+ @Property('htmlAttributes')
156
+ public htmlAttributes: string;
157
+ /**
158
+ * Specifies the `tableName` used to fetch data from a specific table in the server.
159
+ */
160
+ @Property('tableName')
161
+ public tableName: string;
162
+ }
163
+
164
+ /**
165
+ * An interface that holds animation settings.
166
+ */
167
+ export interface AnimationSettings {
168
+ /**
169
+ * It is used to specify the effect which is shown in sub list transform.
170
+ */
171
+ effect?: ListViewEffect;
172
+ /**
173
+ * It is used to specify the time duration of transform object.
174
+ */
175
+ duration?: number;
176
+ /**
177
+ * It is used to specify the easing effect applied while transform
178
+ */
179
+ easing?: string;
180
+ }
181
+
182
+ /**
183
+ * An enum type that denotes the effects of the ListView. Available options are as follows None, SlideLeft, SlideDown, Zoom, Fade;
184
+ */
185
+ export type ListViewEffect = 'None' | 'SlideLeft' | 'SlideDown' | 'Zoom' | 'Fade';
186
+
187
+ /**
188
+ * An enum type that denotes the position of checkbox of the ListView. Available options are as follows Left and Right;
189
+ */
190
+ export type checkBoxPosition = 'Left' | 'Right';
191
+
192
+
193
+ /**
194
+ * Represents the EJ2 ListView control.
195
+ * ```html
196
+ * <div id="listview">
197
+ * <ul>
198
+ * <li>Favorite</li>
199
+ * <li>Documents</li>
200
+ * <li>Downloads</li>
201
+ * </ul>
202
+ * </div>
203
+ * ```
204
+ * ```typescript
205
+ * var listviewObject = new ListView({});
206
+ * listviewObject.appendTo("#listview");
207
+ * ```
208
+ */
209
+ @NotifyPropertyChanges
210
+ export class ListView extends Component<HTMLElement> implements INotifyPropertyChanged {
211
+
212
+ private ulElement: HTMLElement;
213
+ private selectedLI: Element;
214
+ // eslint-disable-next-line
215
+ private onUIScrolled: Function;
216
+ private curUL: HTMLElement;
217
+ private curDSLevel: string[];
218
+ private curViewDS: DataSource[] | string[] | number[];
219
+ private curDSJSON: DataSource;
220
+ public localData: DataSource[];
221
+ private liCollection: HTMLElement[];
222
+ private headerEle: HTMLElement;
223
+ private contentContainer: HTMLElement;
224
+ private touchModule: Touch;
225
+ private listBaseOption: ListBaseOptions;
226
+ public virtualizationModule: Virtualization;
227
+ private animateOptions: AnimationOptions;
228
+ // eslint-disable-next-line
229
+ private rippleFn: Function;
230
+ private isNestedList: boolean;
231
+ private currentLiElements: HTMLElement[];
232
+ private selectedData: string[] | string;
233
+ private selectedId: string[];
234
+ private isWindow: boolean;
235
+ private selectedItems: SelectedItem;
236
+ private aniObj: Animation;
237
+ private LISTVIEW_TEMPLATE_ID: string;
238
+ private LISTVIEW_GROUPTEMPLATE_ID: string;
239
+ private LISTVIEW_HEADERTEMPLATE_ID: string;
240
+ private liElement: Element;
241
+ private virtualCheckBox: Element | string;
242
+ private liElementHeight: number;
243
+ private previousSelectedItems: string[] = [];
244
+ private hiddenItems: string[] = [];
245
+ private enabledItems: string[] = [];
246
+ private disabledItems: string[] = [];
247
+ private isOffline: boolean;
248
+ /**
249
+ * The `cssClass` property is used to add a user-preferred class name in the root element of the ListView,
250
+ * using which we can customize the component (both CSS and functionality customization)
251
+ *
252
+ * {% codeBlock src='listview/cssClass/index.md' %}{% endcodeBlock %}
253
+ *
254
+ * @default ''
255
+ */
256
+ @Property('')
257
+ public cssClass: string;
258
+
259
+ /**
260
+ * If `enableVirtualization` set to true, which will increase the ListView performance, while loading a large amount of data.
261
+ *
262
+ * {% codeBlock src='listview/enableVirtualization/index.md' %}{% endcodeBlock %}
263
+ *
264
+ * @default false
265
+ */
266
+ @Property(false)
267
+ public enableVirtualization: boolean;
268
+
269
+ /**
270
+ * The `htmlAttributes` allows additional attributes such as id, class, etc., and
271
+ * accepts n number of attributes in a key-value pair format.
272
+ *
273
+ * {% codeBlock src='listview/htmlAttributes/index.md' %}{% endcodeBlock %}
274
+ *
275
+ * @default {}
276
+ */
277
+ @Property({})
278
+ public htmlAttributes: { [key: string]: string; };
279
+
280
+ /**
281
+ * If `enable` set to true, the list items are enabled.
282
+ * And, we can disable the component using this property by setting its value as false.
283
+ *
284
+ * {% codeBlock src='listview/enable/index.md' %}{% endcodeBlock %}
285
+ *
286
+ * @default true
287
+ */
288
+ @Property(true)
289
+ public enable: boolean;
290
+ /* es-lint disable */
291
+ /**
292
+ * The `dataSource` provides the data to render the ListView component which is mapped with the fields of ListView.
293
+ *
294
+ * @isGenericType true
295
+ *
296
+ * {% codeBlock src='listview/dataSource/index.md' %}{% endcodeBlock %}
297
+ *
298
+ * @default []
299
+ */
300
+ @Property([])
301
+ public dataSource: { [key: string]: Object }[] | string[] | number[] | DataManager;
302
+ /* es-lint enable */
303
+ /**
304
+ * The `query` is used to fetch the specific data from dataSource by using where and select keywords.
305
+ *
306
+ * {% codeBlock src='listview/query/index.md' %}{% endcodeBlock %}
307
+ *
308
+ * @default null
309
+ */
310
+ @Property()
311
+ public query: Query;
312
+
313
+ /**
314
+ * The `fields` is used to map keys from the dataSource which extracts the appropriate data from the dataSource
315
+ * with specified mapped with the column fields to render the ListView.
316
+ *
317
+ * {% codeBlock src='listview/fields/index.md' %}{% endcodeBlock %}
318
+ *
319
+ * @default defaultMappedFields
320
+ */
321
+ @Complex<FieldSettingsModel>(ListBase.defaultMappedFields, FieldSettings)
322
+ public fields: FieldSettingsModel;
323
+
324
+ /**
325
+ * The `animation` property provides an option to apply the different
326
+ * animations on the ListView component.
327
+ *
328
+ * {% codeBlock src='listview/animation/index.md' %}{% endcodeBlock %}
329
+ *
330
+ *
331
+ * @default { effect: 'SlideLeft', duration: 400, easing: 'ease' }
332
+ */
333
+ @Property<AnimationSettings>({ effect: 'SlideLeft', duration: 400, easing: 'ease' })
334
+ public animation: AnimationSettings;
335
+
336
+ /**
337
+ * The `sortOrder` is used to sort the data source. The available type of sort orders are,
338
+ * * `None` - The data source is not sorting.
339
+ * * `Ascending` - The data source is sorting with ascending order.
340
+ * * `Descending` - The data source is sorting with descending order.
341
+ *
342
+ * {% codeBlock src='listview/sortOrder/index.md' %}{% endcodeBlock %}
343
+ *
344
+ * @default 'None'
345
+ */
346
+ @Property<SortOrder>('None')
347
+ public sortOrder: SortOrder;
348
+
349
+ /**
350
+ * If `showIcon` set to true, which will show or hide the icon of the list item.
351
+ *
352
+ * {% codeBlock src='listview/showIcon/index.md' %}{% endcodeBlock %}
353
+ *
354
+ * @default false
355
+ */
356
+ @Property<boolean>(false)
357
+ public showIcon: boolean;
358
+
359
+ /**
360
+ * If `showCheckBox` set to true, which will show or hide the checkbox.
361
+ *
362
+ * {% codeBlock src='listview/showCheckBox/index.md' %}{% endcodeBlock %}
363
+ *
364
+ *
365
+ * @default false
366
+ */
367
+ @Property<boolean>(false)
368
+ public showCheckBox: boolean;
369
+
370
+ /**
371
+ * The `checkBoxPosition` is used to set the position of check box in a list item.
372
+ * By default, the `checkBoxPosition` is Left, which will appear before the text content in a list item.
373
+ *
374
+ * {% codeBlock src='listview/checkBoxPosition/index.md' %}{% endcodeBlock %}
375
+ *
376
+ * @default 'Left'
377
+ */
378
+ @Property<string>('Left')
379
+ public checkBoxPosition: checkBoxPosition;
380
+
381
+ /**
382
+ * The `headerTitle` is used to set the title of the ListView component.
383
+ *
384
+ * {% codeBlock src='listview/headerTitle/index.md' %}{% endcodeBlock %}
385
+ *
386
+ *
387
+ * @default ""
388
+ */
389
+ @Property<string>('')
390
+ public headerTitle: string;
391
+
392
+ /**
393
+ * If `showHeader` set to true, which will show or hide the header of the ListView component.
394
+ *
395
+ * {% codeBlock src='listview/showHeader/index.md' %}{% endcodeBlock %}
396
+ *
397
+ * @default false
398
+ */
399
+ @Property<boolean>(false)
400
+ public showHeader: boolean;
401
+
402
+ /**
403
+ * If `enableHtmlSanitizer` set to true, allows the cross-scripting site.
404
+ *
405
+ * {% codeBlock src='listview/enableHtmlSanitizer/index.md' %}{% endcodeBlock %}
406
+ *
407
+ * @default false
408
+ */
409
+ @Property(false)
410
+ public enableHtmlSanitizer: boolean;
411
+
412
+ /**
413
+ * Defines the height of the ListView component which accepts both string and number values.
414
+ *
415
+ * {% codeBlock src='listview/height/index.md' %}{% endcodeBlock %}
416
+ *
417
+ * @default ''
418
+ */
419
+ @Property('')
420
+ public height: number | string;
421
+
422
+ /**
423
+ * Defines the width of the ListView component which accepts both string and number values.
424
+ *
425
+ * {% codeBlock src='listview/width/index.md' %}{% endcodeBlock %}
426
+ *
427
+ * @default ''
428
+ */
429
+ @Property('')
430
+ public width: number | string;
431
+
432
+ /**
433
+ * The ListView component supports to customize the content of each list items with the help of `template` property.
434
+ *
435
+ * {% codeBlock src='listview/template/index.md' %}{% endcodeBlock %}
436
+ *
437
+ * @default null
438
+ * @deprecated
439
+ */
440
+ @Property(null)
441
+ public template: string;
442
+
443
+ /**
444
+ * The ListView has an option to custom design the ListView header title with the help of `headerTemplate` property.
445
+ *
446
+ * {% codeBlock src="listview/headerTemplate/index.md" %}{% endcodeBlock %}
447
+ *
448
+ * @default null
449
+ * @deprecated
450
+ */
451
+ @Property(null)
452
+ public headerTemplate: string;
453
+
454
+ /**
455
+ * The ListView has an option to custom design the group header title with the help of `groupTemplate` property.
456
+ *
457
+ * {% codeBlock src="listview/groupTemplate/index.md" %}{% endcodeBlock %}
458
+ *
459
+ * @default null
460
+ * @deprecated
461
+ */
462
+ @Property(null)
463
+ public groupTemplate: string;
464
+
465
+ /**
466
+ * Triggers when we select the list item in the component.
467
+ *
468
+ * @event 'object'
469
+ */
470
+ @Event()
471
+ public select: EmitType<SelectEventArgs>;
472
+
473
+ /**
474
+ * Triggers when every ListView action starts.
475
+ *
476
+ * @event 'object'
477
+ */
478
+ @Event()
479
+ public actionBegin: EmitType<object>;
480
+
481
+ /**
482
+ * Triggers when every ListView actions completed.
483
+ *
484
+ * @event 'object'
485
+ */
486
+ /* es-lint disable */
487
+ @Event()
488
+ public actionComplete: EmitType<MouseEvent>;
489
+ /* es-lint enable */
490
+ /**
491
+ * Triggers, when the data fetch request from the remote server, fails.
492
+ *
493
+ * @event 'object'
494
+ *
495
+ */
496
+ @Event()
497
+ public actionFailure: EmitType<MouseEvent>;
498
+
499
+
500
+ /**
501
+ * Constructor for creating the widget
502
+ *
503
+ * @param options
504
+ *
505
+ * @param element
506
+ */
507
+
508
+ constructor(options?: ListViewModel, element?: string | HTMLElement) {
509
+ super(options, <HTMLElement | string>element);
510
+ }
511
+
512
+ /**
513
+ * @param newProp
514
+ *
515
+ * @param oldProp
516
+ *
517
+ * @private
518
+ */
519
+
520
+ public onPropertyChanged(newProp: ListViewModel, oldProp: ListViewModel): void {
521
+ for (const prop of Object.keys(newProp)) {
522
+ switch (prop) {
523
+ case 'htmlAttributes':
524
+ this.setHTMLAttribute();
525
+ break;
526
+ case 'cssClass':
527
+ this.setCSSClass(oldProp.cssClass);
528
+ break;
529
+ case 'enable':
530
+ this.setEnable();
531
+ break;
532
+ case 'width':
533
+ case 'height':
534
+ this.setSize();
535
+ break;
536
+ case 'enableRtl':
537
+ this.setEnableRTL();
538
+ break;
539
+ case 'fields':
540
+ // eslint-disable-next-line
541
+ this.listBaseOption.fields = (this.fields as any & { properties: object }).properties;
542
+ if (this.enableVirtualization) {
543
+ this.virtualizationModule.reRenderUiVirtualization();
544
+ } else {
545
+ this.reRender();
546
+ }
547
+ break;
548
+ case 'headerTitle':
549
+ if (!this.curDSLevel.length) {
550
+ this.header(this.headerTitle, false, 'header');
551
+ }
552
+ break;
553
+ case 'query':
554
+ if (this.enableVirtualization) {
555
+ this.virtualizationModule.reRenderUiVirtualization();
556
+ } else {
557
+ this.reRender();
558
+ }
559
+ break;
560
+ case 'showHeader':
561
+ this.header(this.headerTitle, false, 'header');
562
+ break;
563
+ case 'enableVirtualization':
564
+ if (!isNullOrUndefined(this.contentContainer)) {
565
+ detach(this.contentContainer);
566
+ }
567
+ this.refresh();
568
+ break;
569
+ case 'showCheckBox':
570
+ case 'checkBoxPosition':
571
+ if (this.enableVirtualization) {
572
+ this.virtualizationModule.reRenderUiVirtualization();
573
+ } else {
574
+ this.setCheckbox();
575
+ }
576
+ break;
577
+ case 'dataSource':
578
+ if (this.enableVirtualization) {
579
+ this.virtualizationModule.reRenderUiVirtualization();
580
+ } else {
581
+ this.reRender();
582
+ }
583
+ break;
584
+ case 'sortOrder':
585
+ case 'template':
586
+ if (!this.enableVirtualization) {
587
+ this.refresh();
588
+ }
589
+ break;
590
+ case 'showIcon':
591
+ if (this.enableVirtualization) {
592
+ this.virtualizationModule.reRenderUiVirtualization();
593
+ } else {
594
+ this.listBaseOption.showIcon = this.showIcon;
595
+ this.curViewDS = this.getSubDS();
596
+ this.resetCurrentList();
597
+ }
598
+ break;
599
+ default:
600
+ break;
601
+ }
602
+ }
603
+ }
604
+
605
+ // Model Changes
606
+ private setHTMLAttribute(): void {
607
+ if (Object.keys(this.htmlAttributes).length) {
608
+ attributes(this.element, this.htmlAttributes);
609
+ }
610
+ }
611
+
612
+ private setCSSClass(oldCSSClass?: string): void {
613
+ if (this.cssClass) {
614
+ addClass([this.element], this.cssClass.split(' ').filter((css: string) => css));
615
+ }
616
+ if (oldCSSClass) {
617
+ removeClass([this.element], oldCSSClass.split(' ').filter((css: string) => css));
618
+ }
619
+ }
620
+
621
+ private setSize(): void {
622
+ this.element.style.height = formatUnit(this.height);
623
+ this.element.style.width = formatUnit(this.width);
624
+ this.isWindow = this.element.clientHeight ? false : true;
625
+ }
626
+
627
+ private setEnable(): void {
628
+ this.enableElement(this.element, this.enable);
629
+ }
630
+
631
+ private setEnableRTL(): void {
632
+ if (this.enableRtl) {
633
+ this.element.classList.add('e-rtl');
634
+ } else {
635
+ this.element.classList.remove('e-rtl');
636
+ }
637
+ }
638
+
639
+ private enableElement(element: HTMLElement, isEnabled?: boolean): void {
640
+ if (isEnabled) {
641
+ element.classList.remove(classNames.disable);
642
+ } else {
643
+ element.classList.add(classNames.disable);
644
+ }
645
+ }
646
+
647
+ // Support Component Functions
648
+ private header(text?: string, showBack?: boolean, prop?: string): void {
649
+ if (this.headerEle === undefined && this.showHeader) {
650
+ if (this.enableHtmlSanitizer) {
651
+ this.setProperties({ headerTitle: SanitizeHtmlHelper.sanitize(this.headerTitle) }, true);
652
+ }
653
+ this.headerEle = this.createElement('div', { className: classNames.header });
654
+ const innerHeaderEle: HTMLElement = this.createElement('span', { className: classNames.headerText, innerHTML: this.headerTitle });
655
+ const textEle: HTMLElement = this.createElement('div', { className: classNames.text, innerHTML: innerHeaderEle.outerHTML });
656
+ const hedBackButton: HTMLElement = this.createElement('div', {
657
+ className: classNames.icon + ' ' + classNames.backIcon + ' ' + classNames.backButton,
658
+ attrs: { style: 'display:none;' }
659
+ });
660
+ this.headerEle.appendChild(hedBackButton);
661
+ this.headerEle.appendChild(textEle);
662
+ if (this.headerTemplate) {
663
+ // eslint-disable-next-line
664
+ const compiledString: Function = compile(this.headerTemplate);
665
+ const headerTemplateEle: HTMLElement = this.createElement('div', { className: classNames.headerTemplateText });
666
+ // eslint-disable-next-line
667
+ const compiledElement: any = compiledString({}, this, prop, this.LISTVIEW_HEADERTEMPLATE_ID, null, null, this.headerEle);
668
+ if (compiledElement) {
669
+ append(compiledElement, headerTemplateEle);
670
+ }
671
+ append([headerTemplateEle], this.headerEle);
672
+ // eslint-disable-next-line
673
+ if ((this as any).isReact) {
674
+ this.renderReactTemplates();
675
+ }
676
+ }
677
+ if (this.headerTemplate && this.headerTitle) {
678
+ textEle.classList.add('header');
679
+ }
680
+ this.element.classList.add('e-has-header');
681
+ prepend([this.headerEle], this.element);
682
+ } else if (this.headerEle) {
683
+ if (this.showHeader) {
684
+ this.headerEle.style.display = '';
685
+ const textEle: Element = this.headerEle.querySelector('.' + classNames.headerText);
686
+ const hedBackButton: Element = this.headerEle.querySelector('.' + classNames.backIcon);
687
+ if (this.enableHtmlSanitizer) {
688
+ text = SanitizeHtmlHelper.sanitize(text);
689
+ }
690
+ textEle.innerHTML = text;
691
+ if (this.headerTemplate && showBack) {
692
+ textEle.parentElement.classList.remove('header');
693
+ this.headerEle.querySelector('.' + classNames.headerTemplateText).classList.add('nested-header');
694
+ }
695
+ if (this.headerTemplate && !showBack) {
696
+ textEle.parentElement.classList.add('header');
697
+ this.headerEle.querySelector('.' + classNames.headerTemplateText).classList.remove('nested-header');
698
+ this.headerEle.querySelector('.' + classNames.headerTemplateText).classList.add('header');
699
+ }
700
+ if (showBack === true) {
701
+ (hedBackButton as HTMLElement).style.display = '';
702
+ } else {
703
+ (hedBackButton as HTMLElement).style.display = 'none';
704
+ }
705
+ } else {
706
+ this.headerEle.style.display = 'none';
707
+ }
708
+ }
709
+ }
710
+
711
+ // Animation Related Functions
712
+ private switchView(fromView: HTMLElement, toView: HTMLElement, reverse?: boolean): void {
713
+ if (fromView && toView) {
714
+ const fPos: string = fromView.style.position;
715
+ const overflow: string = (this.element.style.overflow !== 'hidden') ? this.element.style.overflow : '';
716
+
717
+ fromView.style.position = 'absolute';
718
+ fromView.classList.add('e-view');
719
+ let anim: Effect[];
720
+ let duration: number = this.animation.duration;
721
+ if (this.animation.effect) {
722
+ anim = (this.enableRtl ? effectsRTLConfig[this.animation.effect] : effectsConfig[this.animation.effect]);
723
+ } else {
724
+ const slideLeft: string = 'SlideLeft';
725
+ anim = effectsConfig[slideLeft];
726
+ reverse = this.enableRtl;
727
+ duration = 0;
728
+ }
729
+ this.element.style.overflow = 'hidden';
730
+ this.aniObj.animate(fromView, {
731
+ name: (reverse === true ? anim[0] : anim[1]),
732
+ duration: duration,
733
+ timingFunction: this.animation.easing,
734
+ // eslint-disable-next-line
735
+ end: (model: AnimationOptions): void => {
736
+ fromView.style.display = 'none';
737
+ this.element.style.overflow = overflow;
738
+ fromView.style.position = fPos;
739
+ fromView.classList.remove('e-view');
740
+ }
741
+ });
742
+ toView.style.display = '';
743
+ this.aniObj.animate(toView, {
744
+ name: (reverse === true ? anim[2] : anim[3]),
745
+ duration: duration,
746
+ timingFunction: this.animation.easing,
747
+ end: (): void => {
748
+ this.trigger('actionComplete');
749
+ }
750
+ });
751
+ this.curUL = toView;
752
+ }
753
+ }
754
+
755
+ protected preRender(): void {
756
+ if (this.template) {
757
+ try {
758
+ if (document.querySelectorAll(this.template).length) {
759
+ this.setProperties({ template: document.querySelector(this.template).innerHTML.trim() }, true);
760
+ }
761
+ } catch (e) {
762
+ compile(this.template);
763
+ }
764
+ }
765
+ this.listBaseOption = {
766
+ template: this.template,
767
+ headerTemplate: this.headerTemplate,
768
+ groupTemplate: this.groupTemplate, expandCollapse: true, listClass: '',
769
+ ariaAttributes: {
770
+ itemRole: 'option', listRole: 'presentation', itemText: '',
771
+ groupItemRole: 'option', wrapperRole: 'presentation'
772
+ },
773
+ // eslint-disable-next-line
774
+ fields: ((this.fields as any & { properties: Object }).properties) as FieldsMapping,
775
+ sortOrder: this.sortOrder,
776
+ showIcon: this.showIcon,
777
+ itemCreated: this.renderCheckbox.bind(this),
778
+ templateID: `${this.element.id}${LISTVIEW_TEMPLATE_PROPERTY}`,
779
+ groupTemplateID: `${this.element.id}${LISTVIEW_GROUPTEMPLATE_PROPERTY}`,
780
+ enableHtmlSanitizer: this.enableHtmlSanitizer,
781
+ };
782
+ this.initialization();
783
+ }
784
+
785
+ private initialization(): void {
786
+ this.curDSLevel = [];
787
+ this.animateOptions = {};
788
+ this.curViewDS = [];
789
+ this.currentLiElements = [];
790
+ this.isNestedList = false;
791
+ this.selectedData = [];
792
+ this.selectedId = [];
793
+ this.LISTVIEW_TEMPLATE_ID = `${this.element.id}${LISTVIEW_TEMPLATE_PROPERTY}`;
794
+ this.LISTVIEW_GROUPTEMPLATE_ID = `${this.element.id}${LISTVIEW_GROUPTEMPLATE_PROPERTY}`;
795
+ this.LISTVIEW_HEADERTEMPLATE_ID = `${this.element.id}${LISTVIEW_HEADERTEMPLATE_PROPERTY}`;
796
+ this.aniObj = new Animation(this.animateOptions);
797
+ this.removeElement(this.curUL);
798
+ this.removeElement(this.ulElement);
799
+ this.removeElement(this.headerEle);
800
+ this.removeElement(this.contentContainer);
801
+ this.curUL = this.ulElement = this.liCollection = this.headerEle = this.contentContainer = undefined;
802
+ }
803
+
804
+ private renderCheckbox(args: ItemCreatedArgs): void {
805
+ if (args.item.classList.contains(classNames.hasChild)) {
806
+ this.isNestedList = true;
807
+ }
808
+ if (this.showCheckBox && this.isValidLI(args.item)) {
809
+ let checkboxElement: Element;
810
+ let fieldData: DataSource;
811
+ // eslint-disable-next-line prefer-const
812
+ checkboxElement = createCheckBox(this.createElement, false, {
813
+ checked: false, enableRtl: this.enableRtl,
814
+ cssClass: classNames.listviewCheckbox
815
+ });
816
+ checkboxElement.setAttribute('role', 'checkbox');
817
+ const frameElement: Element = checkboxElement.querySelector('.' + classNames.checkboxIcon);
818
+ args.item.classList.add(classNames.itemCheckList);
819
+ args.item.firstElementChild.classList.add(classNames.checkbox);
820
+ if (typeof (this.dataSource as string[])[0] !== 'string' && typeof (this.dataSource as number[])[0] !== 'number') {
821
+ fieldData = <DataSource>getFieldValues(args.curData, this.listBaseOption.fields);
822
+ if (<object>fieldData[this.listBaseOption.fields.isChecked]) {
823
+ this.checkInternally(args, checkboxElement);
824
+ }
825
+ } else if (((typeof (this.dataSource as string[])[0] === 'string' ||
826
+ typeof (this.dataSource as number[])[0] === 'number') && this.selectedData.indexOf(args.text) !== -1)) {
827
+ this.checkInternally(args, checkboxElement);
828
+ }
829
+ checkboxElement.setAttribute('aria-checked', frameElement.classList.contains(classNames.checked) ? 'true' : 'false');
830
+ if (this.checkBoxPosition === 'Left') {
831
+ checkboxElement.classList.add(classNames.checkboxLeft);
832
+ args.item.firstElementChild.classList.add(classNames.checkboxLeft);
833
+ args.item.firstElementChild.insertBefore(checkboxElement, args.item.firstElementChild.childNodes[0]);
834
+ } else {
835
+ checkboxElement.classList.add(classNames.checkboxRight);
836
+ args.item.firstElementChild.classList.add(classNames.checkboxRight);
837
+ args.item.firstElementChild.appendChild(checkboxElement);
838
+ }
839
+ this.currentLiElements.push(args.item);
840
+ if (this.checkBoxPosition === 'Left') {
841
+ this.virtualCheckBox = args.item.firstElementChild.children[0];
842
+ }
843
+ else {
844
+ this.virtualCheckBox = args.item.firstElementChild.lastElementChild;
845
+ }
846
+ }
847
+ }
848
+
849
+ private checkInternally(args: ItemCreatedArgs, checkboxElement: Element): void {
850
+ args.item.classList.add(classNames.selected);
851
+ args.item.setAttribute('aria-selected', 'true');
852
+ checkboxElement.querySelector('.' + classNames.checkboxIcon).classList.add(classNames.checked);
853
+ checkboxElement.setAttribute('aria-checked', 'true');
854
+ }
855
+
856
+ /**
857
+ * Checks the specific list item by passing the unchecked fields as an argument to this method.
858
+ *
859
+ * @param {Fields | HTMLElement | Element} item - It accepts Fields or HTML list element as an argument.
860
+ */
861
+
862
+ public checkItem(item: Fields | HTMLElement | Element): void {
863
+ this.toggleCheckBase(item, true);
864
+ }
865
+
866
+ private toggleCheckBase(item: Fields | Element | HTMLElement, checked: boolean): void {
867
+ if (this.showCheckBox) {
868
+ let liElement: Element = item as Element;
869
+ if (item instanceof Object && (item as Object).constructor !== HTMLLIElement) {
870
+ liElement = this.getLiFromObjOrElement(item);
871
+ }
872
+ if (!isNullOrUndefined(liElement)) {
873
+ const checkboxIcon: Element = liElement.querySelector('.' + classNames.checkboxIcon);
874
+ if (checked === true) {
875
+ liElement.classList.add(classNames.selected);
876
+ }
877
+ else {
878
+ liElement.classList.remove(classNames.selected);
879
+ }
880
+ liElement.setAttribute('aria-selected', checked ? 'true' : 'false');
881
+ if (checked === true) {
882
+ checkboxIcon.classList.add(classNames.checked);
883
+ }
884
+ else {
885
+ checkboxIcon.classList.remove(classNames.checked);
886
+ }
887
+ checkboxIcon.parentElement.setAttribute('aria-checked', checked ? 'true' : 'false');
888
+ }
889
+ this.setSelectedItemData(liElement);
890
+ }
891
+ }
892
+
893
+ /**
894
+ * Uncheck the specific list item by passing the checked fields as an argument to this method.
895
+ *
896
+ * @param {Fields | HTMLElement | Element} item - It accepts Fields or HTML list element as an argument.
897
+ */
898
+
899
+ public uncheckItem(item: Fields | HTMLElement | Element): void {
900
+ this.toggleCheckBase(item, false);
901
+ }
902
+
903
+ /**
904
+ * Checks all the unchecked items in the ListView.
905
+ */
906
+
907
+ public checkAllItems(): void {
908
+ this.toggleAllCheckBase(true);
909
+ }
910
+
911
+ /**
912
+ * Uncheck all the checked items in ListView.
913
+ */
914
+
915
+ public uncheckAllItems(): void {
916
+ this.toggleAllCheckBase(false);
917
+ }
918
+
919
+ private toggleAllCheckBase(checked: boolean): void {
920
+ if (this.showCheckBox) {
921
+ for (let i: number = 0; i < this.liCollection.length; i++) {
922
+ const checkIcon: Element = this.liCollection[i].querySelector('.' + classNames.checkboxIcon);
923
+ if (checkIcon) {
924
+ if (checked) {
925
+ if (!checkIcon.classList.contains(classNames.checked)) {
926
+ this.checkItem(this.liCollection[i]);
927
+ }
928
+ } else {
929
+ if (checkIcon.classList.contains(classNames.checked)) {
930
+ this.uncheckItem(this.liCollection[i]);
931
+ }
932
+ }
933
+ }
934
+ }
935
+ if (this.enableVirtualization) {
936
+ this.virtualizationModule.checkedItem(checked);
937
+ }
938
+ }
939
+ }
940
+
941
+ private setCheckbox(): void {
942
+ if (this.showCheckBox) {
943
+ const liCollection: HTMLElement[] = Array.prototype.slice.call(this.element.querySelectorAll('.' + classNames.listItem));
944
+ const args: ItemCreatedArgs = {
945
+ item: undefined, curData: undefined, dataSource: undefined, fields: undefined,
946
+ options: undefined, text: ''
947
+ };
948
+ for (let i: number = 0; i < liCollection.length; i++) {
949
+ const element: HTMLElement = liCollection[i];
950
+ args.item = element;
951
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
952
+ args.curData = this.getItemData(element) as { [key: string]: Object } as any;
953
+ if (element.querySelector('.' + classNames.checkboxWrapper)) {
954
+ this.removeElement(element.querySelector('.' + classNames.checkboxWrapper));
955
+ }
956
+ this.renderCheckbox(args);
957
+ if (args.item.classList.contains(classNames.selected)) {
958
+ this.checkInternally(args, args.item.querySelector('.' + classNames.checkboxWrapper));
959
+ }
960
+ }
961
+ } else {
962
+ const liCollection: HTMLElement[] = Array.prototype.slice.call(this.element.querySelectorAll('.' + classNames.itemCheckList));
963
+ for (let i: number = 0; i < liCollection.length; i++) {
964
+ const element: HTMLElement = liCollection[i];
965
+ element.classList.remove(classNames.selected);
966
+ element.firstElementChild.classList.remove(classNames.checkbox);
967
+ this.removeElement(element.querySelector('.' + classNames.checkboxWrapper));
968
+ }
969
+ if (this.selectedItems) {
970
+ this.selectedItems.item.classList.add(classNames.selected);
971
+ }
972
+ }
973
+ }
974
+
975
+ /**
976
+ * Refresh the height of the list item only on enabling the virtualization property.
977
+ */
978
+
979
+ public refreshItemHeight(): void {
980
+ if (this.virtualizationModule) {
981
+ this.virtualizationModule.refreshItemHeight();
982
+ }
983
+ }
984
+
985
+ private clickHandler(e: MouseEvent): void {
986
+ const target: Element = <Element>e.target;
987
+ const classList: DOMTokenList = target.classList;
988
+ let closestElement: HTMLElement;
989
+ if (classList.contains(classNames.backIcon) || classList.contains(classNames.headerText)) {
990
+ if (this.showCheckBox && this.curDSLevel[this.curDSLevel.length - 1]) {
991
+ this.uncheckAllItems();
992
+ }
993
+ this.back();
994
+ } else {
995
+ let li: HTMLElement = <HTMLElement>closest(target.parentNode, '.' + classNames.listItem);
996
+ if (li === null) { li = <HTMLElement>target; }
997
+ this.removeFocus();
998
+ if (this.enable && this.showCheckBox && this.isValidLI(li)) {
999
+ if ((e.target as HTMLElement).classList.contains(classNames.checkboxIcon)) {
1000
+ li.classList.add(classNames.focused);
1001
+ if (isNullOrUndefined(li.querySelector('.' + classNames.checked))) {
1002
+ const args: ItemCreatedArgs = {
1003
+ curData: undefined, dataSource: undefined, fields: undefined, options: undefined,
1004
+ text: undefined, item: li
1005
+ };
1006
+ this.checkInternally(args, args.item.querySelector('.' + classNames.checkboxWrapper));
1007
+ } else {
1008
+ this.uncheckItem(li);
1009
+ li.classList.add(classNames.focused);
1010
+ }
1011
+ if (this.enableVirtualization) {
1012
+ this.virtualizationModule.setCheckboxLI(li, e);
1013
+ }
1014
+ if (e) {
1015
+ const eventArgs: Object = this.selectEventData(li, e);
1016
+ const checkIcon: Element = li.querySelector('.' + classNames.checkboxIcon);
1017
+ merge(eventArgs, { isChecked: checkIcon.classList.contains(classNames.checked) });
1018
+ this.trigger('select', eventArgs);
1019
+ }
1020
+ } else if (li.classList.contains(classNames.hasChild)) {
1021
+ this.removeHover();
1022
+ this.removeSelect();
1023
+ this.removeSelect(li);
1024
+ this.setSelectLI(li, e);
1025
+ li.classList.remove(classNames.selected);
1026
+ } else {
1027
+ this.setCheckboxLI(li, e);
1028
+ }
1029
+ } else {
1030
+ this.setSelectLI(li, e);
1031
+ this.element.focus();
1032
+ }
1033
+ closestElement = closest((e.target as HTMLElement), 'li') as HTMLElement;
1034
+ if (!isNullOrUndefined(closestElement)) {
1035
+ if (closestElement.classList.contains('e-has-child') &&
1036
+ !(e.target as HTMLElement).parentElement.classList.contains('e-listview-checkbox')) {
1037
+ closestElement.classList.add(classNames.disable);
1038
+ }
1039
+ }
1040
+ }
1041
+ }
1042
+
1043
+ private removeElement(element: HTMLElement | Element): HTMLElement | Element {
1044
+ return element && element.parentNode && element.parentNode.removeChild(element);
1045
+ }
1046
+
1047
+ private hoverHandler(e: MouseEvent): void {
1048
+ const curLi: HTMLElement = <HTMLElement>closest((<Element>e.target).parentNode, '.' + classNames.listItem);
1049
+ this.setHoverLI(curLi);
1050
+ }
1051
+
1052
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1053
+ private leaveHandler(e: MouseEvent): void {
1054
+ this.removeHover();
1055
+ }
1056
+ private homeKeyHandler(e: KeyboardEventArgs, end?: boolean): void {
1057
+ e.preventDefault();
1058
+ if (Object.keys(this.dataSource).length && this.curUL) {
1059
+ if (this.selectedItems) {
1060
+ (this.selectedItems.item).setAttribute('aria-selected', 'false');
1061
+ }
1062
+ const li: Element[] = <NodeListOf<Element> & Element[]>this.curUL.querySelectorAll('.' + classNames.listItem);
1063
+ const focusedElement: Element = this.curUL.querySelector('.' + classNames.focused) ||
1064
+ this.curUL.querySelector('.' + classNames.selected);
1065
+ if (focusedElement) {
1066
+ focusedElement.classList.remove(classNames.focused);
1067
+ if (!this.showCheckBox) {
1068
+ focusedElement.classList.remove(classNames.selected);
1069
+ }
1070
+ }
1071
+ const index: number = !end ? 0 : li.length - 1;
1072
+ if (li[index].classList.contains(classNames.hasChild) || this.showCheckBox) {
1073
+ li[index].classList.add(classNames.focused);
1074
+ } else {
1075
+ this.setSelectLI(li[index], e);
1076
+ }
1077
+ if (li[index]) {
1078
+ this.element.setAttribute('aria-activedescendant', (<HTMLElement>li[index]).id.toString());
1079
+ } else {
1080
+ this.element.removeAttribute('aria-activedescendant');
1081
+ }
1082
+ }
1083
+ }
1084
+
1085
+ private onArrowKeyDown(e: KeyboardEventArgs, prev: boolean): Element {
1086
+ let siblingLI: Element;
1087
+ let li: Element;
1088
+ const hasChild: boolean = !isNullOrUndefined(this.curUL.querySelector('.' + classNames.hasChild)) ? true : false;
1089
+ if (hasChild || this.showCheckBox) {
1090
+ li = this.curUL.querySelector('.' + classNames.focused) || this.curUL.querySelector('.' + classNames.selected);
1091
+ siblingLI = ListBase.getSiblingLI(this.curUL.querySelectorAll('.' + classNames.listItem), li, prev);
1092
+ if (!isNullOrUndefined(siblingLI)) {
1093
+ if (li) {
1094
+ li.classList.remove(classNames.focused);
1095
+ if (!this.showCheckBox) {
1096
+ li.classList.remove(classNames.selected);
1097
+ }
1098
+ }
1099
+ if (siblingLI.classList.contains(classNames.hasChild) || this.showCheckBox) {
1100
+ siblingLI.classList.add(classNames.focused);
1101
+ } else {
1102
+ this.setSelectLI(siblingLI, e);
1103
+ }
1104
+ }
1105
+ } else {
1106
+ li = this.curUL.querySelector('.' + classNames.selected);
1107
+ siblingLI = ListBase.getSiblingLI(this.curUL.querySelectorAll('.' + classNames.listItem), li, prev);
1108
+ this.setSelectLI(siblingLI, e);
1109
+ }
1110
+ if (siblingLI) {
1111
+ this.element.setAttribute('aria-activedescendant', (<HTMLElement>siblingLI).id.toString());
1112
+ } else {
1113
+ this.element.removeAttribute('aria-activedescendant');
1114
+ }
1115
+ return siblingLI;
1116
+ }
1117
+
1118
+ private arrowKeyHandler(e: KeyboardEventArgs, prev?: boolean): void {
1119
+ e.preventDefault();
1120
+ if (Object.keys(this.dataSource).length && this.curUL) {
1121
+ const siblingLI: Element = this.onArrowKeyDown(e, prev);
1122
+ const elementTop: number = this.element.getBoundingClientRect().top;
1123
+ const elementHeight: number = this.element.getBoundingClientRect().height;
1124
+ const firstItemBounds: ClientRect = this.curUL.querySelector('.' + classNames.listItem).getBoundingClientRect();
1125
+ let heightDiff: number;
1126
+ let groupItemBounds: ClientRect;
1127
+ if (this.fields.groupBy) {
1128
+ groupItemBounds = this.curUL.querySelector('.' + classNames.groupListItem).getBoundingClientRect();
1129
+ }
1130
+ if (siblingLI) {
1131
+ const siblingTop: number = siblingLI.getBoundingClientRect().top;
1132
+ const siblingHeight: number = siblingLI.getBoundingClientRect().height;
1133
+ if (!prev) {
1134
+ const height: number = this.isWindow ? window.innerHeight : elementHeight;
1135
+ heightDiff = this.isWindow ? (siblingTop + siblingHeight) :
1136
+ ((siblingTop - elementTop) + siblingHeight);
1137
+ if (heightDiff > height) {
1138
+ if (this.isWindow === true) {
1139
+ window.scroll(0, pageYOffset + (heightDiff - height));
1140
+ }
1141
+ else {
1142
+ this.element.scrollTop = this.element.scrollTop + (heightDiff - height);
1143
+ }
1144
+ }
1145
+ } else {
1146
+ heightDiff = this.isWindow ? siblingTop : (siblingTop - elementTop);
1147
+ if (heightDiff < 0) {
1148
+ if (this.isWindow === true) {
1149
+ window.scroll(0, pageYOffset + heightDiff);
1150
+ }
1151
+ else {
1152
+ this.element.scrollTop = this.element.scrollTop + heightDiff;
1153
+ }
1154
+ }
1155
+ }
1156
+ } else if (this.enableVirtualization && prev && this.virtualizationModule.uiFirstIndex) {
1157
+ this.onUIScrolled = () => {
1158
+ this.onArrowKeyDown(e, prev);
1159
+ this.onUIScrolled = undefined;
1160
+ };
1161
+ heightDiff = this.virtualizationModule.listItemHeight;
1162
+ if (this.isWindow === true) {
1163
+ window.scroll(0, pageYOffset - heightDiff);
1164
+ }
1165
+ else {
1166
+ this.element.scrollTop = this.element.scrollTop - heightDiff;
1167
+ }
1168
+ } else if (prev) {
1169
+ if (this.showHeader && this.headerEle) {
1170
+ const topHeight: number = groupItemBounds ? groupItemBounds.top : firstItemBounds.top;
1171
+ const headerBounds: ClientRect = this.headerEle.getBoundingClientRect();
1172
+ heightDiff = headerBounds.top < 0 ? (headerBounds.height - topHeight) : 0;
1173
+ if (this.isWindow === true) {
1174
+ window.scroll(0, pageYOffset - heightDiff);
1175
+ }
1176
+ else {
1177
+ this.element.scrollTop = 0;
1178
+ }
1179
+ } else if (this.fields.groupBy) {
1180
+ heightDiff = this.isWindow ? (groupItemBounds.top < 0 ? groupItemBounds.top : 0) :
1181
+ (elementTop - firstItemBounds.top) + groupItemBounds.height;
1182
+ if (this.isWindow === true) {
1183
+ window.scroll(0, pageYOffset + heightDiff);
1184
+ }
1185
+ else {
1186
+ this.element.scrollTop = this.element.scrollTop - heightDiff;
1187
+ }
1188
+ }
1189
+ }
1190
+ }
1191
+ }
1192
+
1193
+ private enterKeyHandler(e: KeyboardEventArgs): void {
1194
+ if (Object.keys(this.dataSource).length && this.curUL) {
1195
+ const hasChild: boolean = !isNullOrUndefined(this.curUL.querySelector('.' + classNames.hasChild)) ? true : false;
1196
+ const li: Element = this.curUL.querySelector('.' + classNames.focused);
1197
+ if (hasChild && li) {
1198
+ li.classList.remove(classNames.focused);
1199
+ if (this.showCheckBox) {
1200
+ this.removeSelect();
1201
+ this.removeSelect(<HTMLElement>li);
1202
+ this.removeHover();
1203
+ }
1204
+ this.setSelectLI(li, e);
1205
+ }
1206
+ }
1207
+ }
1208
+ private spaceKeyHandler(e: KeyboardEventArgs): void {
1209
+ e.preventDefault();
1210
+ if (this.enable && this.showCheckBox && Object.keys(this.dataSource).length && this.curUL) {
1211
+ const li: Element = this.curUL.querySelector('.' + classNames.focused);
1212
+ let checkboxElement: Element;
1213
+ let checkIcon: Element;
1214
+ if (!isNullOrUndefined(li) && isNullOrUndefined(li.querySelector('.' + classNames.checked))) {
1215
+ const args: ItemCreatedArgs = {
1216
+ curData: undefined, dataSource: undefined, fields: undefined, options: undefined,
1217
+ text: undefined, item: <HTMLElement>li
1218
+ };
1219
+ checkboxElement = args.item.querySelector('.' + classNames.checkboxWrapper);
1220
+ this.checkInternally(args, checkboxElement);
1221
+ checkIcon = checkboxElement.querySelector('.' + classNames.checkboxIcon + '.' + classNames.icon);
1222
+ } else {
1223
+ this.uncheckItem(li);
1224
+ }
1225
+ const eventArgs: object = this.selectEventData(li, e);
1226
+ merge(eventArgs, { isChecked: checkIcon ? checkIcon.classList.contains(classNames.checked) : false });
1227
+ this.trigger('select', eventArgs);
1228
+ }
1229
+ }
1230
+
1231
+ private keyActionHandler(e: KeyboardEventArgs): void {
1232
+ switch (e.keyCode) {
1233
+ case 36:
1234
+ this.homeKeyHandler(e);
1235
+ break;
1236
+ case 35:
1237
+ this.homeKeyHandler(e, true);
1238
+ break;
1239
+ case 40:
1240
+ this.arrowKeyHandler(e);
1241
+ break;
1242
+ case 38:
1243
+ this.arrowKeyHandler(e, true);
1244
+ break;
1245
+ case 13:
1246
+ this.enterKeyHandler(e);
1247
+ break;
1248
+ case 8:
1249
+ if (this.showCheckBox && this.curDSLevel[this.curDSLevel.length - 1]) {
1250
+ this.uncheckAllItems();
1251
+ }
1252
+ this.back();
1253
+ break;
1254
+ case 32:
1255
+ this.spaceKeyHandler(e);
1256
+ break;
1257
+ }
1258
+ }
1259
+
1260
+ private swipeActionHandler(e: SwipeEventArgs): void {
1261
+ if (e.swipeDirection === 'Right' && e.velocity > swipeVelocity && e.originalEvent.type === 'touchend') {
1262
+ if (this.showCheckBox && this.curDSLevel[this.curDSLevel.length - 1]) {
1263
+ this.uncheckAllItems();
1264
+ }
1265
+ this.back();
1266
+ }
1267
+ }
1268
+
1269
+ private focusout(): void {
1270
+ if (Object.keys(this.dataSource).length && this.curUL) {
1271
+ const focusedElement: Element = this.curUL.querySelector('.' + classNames.focused);
1272
+ const activeElement: Element = this.curUL.querySelector('[aria-selected = true]');
1273
+ if (focusedElement) {
1274
+ focusedElement.classList.remove(classNames.focused);
1275
+ if (activeElement && !this.showCheckBox) {
1276
+ activeElement.classList.add(classNames.selected);
1277
+ }
1278
+ }
1279
+ }
1280
+ }
1281
+
1282
+ private wireEvents(): void {
1283
+ EventHandler.add(this.element, 'keydown', this.keyActionHandler, this);
1284
+ EventHandler.add(this.element, 'click', this.clickHandler, this);
1285
+ EventHandler.add(this.element, 'mouseover', this.hoverHandler, this);
1286
+ EventHandler.add(this.element, 'mouseout', this.leaveHandler, this);
1287
+ EventHandler.add(this.element, 'focusout', this.focusout, this);
1288
+ this.touchModule = new Touch(this.element, { swipe: this.swipeActionHandler.bind(this) });
1289
+ }
1290
+
1291
+ private unWireEvents(): void {
1292
+ EventHandler.remove(this.element, 'click', this.clickHandler);
1293
+ EventHandler.remove(this.element, 'mouseover', this.hoverHandler);
1294
+ EventHandler.remove(this.element, 'mouseout', this.leaveHandler);
1295
+ EventHandler.remove(this.element, 'mouseover', this.hoverHandler);
1296
+ EventHandler.remove(this.element, 'mouseout', this.leaveHandler);
1297
+ this.touchModule.destroy();
1298
+ }
1299
+
1300
+ private removeFocus(): void {
1301
+ const focusedLI: Element[] = <NodeListOf<Element> & Element[]>this.element.querySelectorAll('.' + classNames.focused);
1302
+ for (const ele of focusedLI) {
1303
+ ele.classList.remove(classNames.focused);
1304
+ }
1305
+ }
1306
+
1307
+ private removeHover(): void {
1308
+ const hoverLI: Element = this.element.querySelector('.' + classNames.hover);
1309
+ if (hoverLI) { hoverLI.classList.remove(classNames.hover); }
1310
+ }
1311
+
1312
+ private removeSelect(li?: HTMLElement): void {
1313
+ if (isNullOrUndefined(li)) {
1314
+ const selectedLI: Element[] = <NodeListOf<Element> & Element[]>this.element.querySelectorAll('.' + classNames.selected);
1315
+ for (const ele of selectedLI) {
1316
+ if (this.showCheckBox && ele.querySelector('.' + classNames.checked)) {
1317
+ continue;
1318
+ } else {
1319
+ ele.setAttribute('aria-selected', 'false');
1320
+ ele.classList.remove(classNames.selected);
1321
+ }
1322
+ }
1323
+ } else {
1324
+ li.classList.remove(classNames.selected);
1325
+ li.setAttribute('aria-selected', 'false');
1326
+ }
1327
+ }
1328
+
1329
+ private isValidLI(li: Element | HTMLElement): boolean {
1330
+ return (li && li.classList.contains(classNames.listItem)
1331
+ && !li.classList.contains(classNames.groupListItem)
1332
+ && !li.classList.contains(classNames.disable));
1333
+ }
1334
+
1335
+ private setCheckboxLI(li: Element, e?: MouseEvent | KeyboardEvent | FocusEvent): void {
1336
+ if (this.isValidLI(li) && this.enable && this.showCheckBox) {
1337
+ if (this.curUL.querySelector('.' + classNames.focused)) {
1338
+ this.curUL.querySelector('.' + classNames.focused).classList.remove(classNames.focused);
1339
+ }
1340
+ li.classList.add(classNames.focused);
1341
+ const checkboxElement: Element = li.querySelector('.' + classNames.checkboxWrapper);
1342
+ const checkIcon: Element = checkboxElement.querySelector('.' + classNames.checkboxIcon + '.' + classNames.icon);
1343
+ this.removeHover();
1344
+ if (!checkIcon.classList.contains(classNames.checked)) {
1345
+ checkIcon.classList.add(classNames.checked);
1346
+ li.classList.add(classNames.selected);
1347
+ li.setAttribute('aria-selected', 'true');
1348
+ } else {
1349
+ checkIcon.classList.remove(classNames.checked);
1350
+ li.classList.remove(classNames.selected);
1351
+ li.setAttribute('aria-selected', 'false');
1352
+ }
1353
+ checkboxElement.setAttribute('aria-checked', checkIcon.classList.contains(classNames.checked) ?
1354
+ 'true' : 'false');
1355
+ const eventArgs: object = this.selectEventData(li, e);
1356
+ merge(eventArgs, { isChecked: checkIcon.classList.contains(classNames.checked) });
1357
+ if (this.enableVirtualization) {
1358
+ this.virtualizationModule.setCheckboxLI(li, e);
1359
+ }
1360
+ this.trigger('select', eventArgs);
1361
+ this.setSelectedItemData(li);
1362
+ this.renderSubList(li);
1363
+ }
1364
+ }
1365
+
1366
+ private selectEventData(li: Element, e?: MouseEvent | KeyboardEvent | FocusEvent): Object {
1367
+ const data: DataSource = this.getItemData(li);
1368
+ const fieldData: DataSource = getFieldValues(data as { [key: string]: Object }, this.listBaseOption.fields) as DataSource;
1369
+ let selectedItem: SelectedItem;
1370
+ if (!isNullOrUndefined(data)
1371
+ && typeof (this.dataSource as string[])[0] === 'string' || typeof (this.dataSource as number[])[0] === 'number') {
1372
+ selectedItem = { item: li, text: li && (li as HTMLElement).innerText.trim(), data: this.dataSource as string[] | number[] };
1373
+ } else {
1374
+ selectedItem =
1375
+ // eslint-disable-next-line
1376
+ {
1377
+ item: li, text: fieldData && <object>fieldData[this.listBaseOption.fields.text] as any,
1378
+ // eslint-disable-next-line
1379
+ data: data as { [key: string]: Object } as any
1380
+ };
1381
+ }
1382
+ const eventArgs: object = {};
1383
+ merge(eventArgs, selectedItem);
1384
+ if (e) {
1385
+ merge(eventArgs, { isInteracted: true, event: e, index: this.curUL && Array.prototype.indexOf.call(this.curUL.children, li) });
1386
+ }
1387
+ return eventArgs;
1388
+ }
1389
+
1390
+ private setSelectedItemData(li: Element): void {
1391
+ const data: DataSource = this.getItemData(li);
1392
+ // eslint-disable-next-line
1393
+ const fieldData: DataSource = <DataSource>getFieldValues(data as { [key: string]: Object } as any, this.listBaseOption.fields);
1394
+ if (!isNullOrUndefined(data) && ((typeof (this.dataSource as string[])[0] === 'string') ||
1395
+ (typeof (this.dataSource as number[])[0] === 'number'))) {
1396
+ this.selectedItems = {
1397
+ item: li,
1398
+ text: li && (li as HTMLElement).innerText.trim(),
1399
+ data: this.dataSource as string[]
1400
+ };
1401
+ } else {
1402
+ this.selectedItems = {
1403
+ item: li,
1404
+ // eslint-disable-next-line
1405
+ text: fieldData && fieldData[this.listBaseOption.fields.text] as any,
1406
+ // eslint-disable-next-line
1407
+ data: data as { [key: string]: Object } as any
1408
+ };
1409
+ }
1410
+ }
1411
+
1412
+ private setSelectLI(li: Element, e?: MouseEvent | KeyboardEvent | FocusEvent): void {
1413
+ if (this.isValidLI(li) && !li.classList.contains(classNames.selected) && this.enable) {
1414
+ if (!this.showCheckBox) {
1415
+ this.removeSelect();
1416
+ }
1417
+ li.classList.add(classNames.selected);
1418
+ li.setAttribute('aria-selected', 'true');
1419
+ this.removeHover();
1420
+ this.setSelectedItemData(li);
1421
+ if (this.enableVirtualization) {
1422
+ this.virtualizationModule.setSelectLI(li, e);
1423
+ }
1424
+ const eventArgs: object = this.selectEventData(li, e);
1425
+ this.trigger('select', eventArgs);
1426
+ this.selectedLI = li;
1427
+ this.renderSubList(li);
1428
+ }
1429
+ }
1430
+
1431
+ private setHoverLI(li: Element): void {
1432
+ if (this.isValidLI(li) && !li.classList.contains(classNames.hover) && this.enable) {
1433
+ const lastLi: Element[] = <NodeListOf<Element> & Element[]>this.element.querySelectorAll('.' + classNames.hover);
1434
+ if (lastLi && lastLi.length) { removeClass(lastLi, classNames.hover); }
1435
+ if (!(li as Element).classList.contains(classNames.selected) || this.showCheckBox) {
1436
+ (li as Element).classList.add(classNames.hover);
1437
+ }
1438
+ }
1439
+ }
1440
+
1441
+ //Data Source Related Functions
1442
+ private getSubDS(): DataSource[] {
1443
+ const levelKeys: string[] = this.curDSLevel;
1444
+ if (levelKeys.length) {
1445
+ let ds: DataSource[] = <DataSource[]>this.localData;
1446
+ for (const key of levelKeys) {
1447
+ const field: DataSource = {};
1448
+ // eslint-disable-next-line
1449
+ field[this.fields.id] = key as any;
1450
+ this.curDSJSON = <DataSource[] & DataSource>this.findItemFromDS(ds, field);
1451
+ const fieldData: DataSource = <DataSource>getFieldValues
1452
+ // eslint-disable-next-line no-unexpected-multiline
1453
+ (this.curDSJSON as { [key: string]: Object }, this.listBaseOption.fields);
1454
+ ds = this.curDSJSON ? <DataSource[] & DataSource>fieldData[this.fields.child] : ds;
1455
+ }
1456
+ return ds;
1457
+ }
1458
+ return <DataSource[]>this.localData;
1459
+ }
1460
+
1461
+ private getItemData(li: Element | HTMLElement | Fields): DataSource {
1462
+ const dataSource: DataSource[] | string[] | number[] | DataManager = this.dataSource instanceof DataManager ?
1463
+ this.localData : this.dataSource;
1464
+ const fields: Fields = this.getElementUID(li);
1465
+ let curDS: DataSource[];
1466
+ if (isNullOrUndefined(this.element.querySelector('.' + classNames.hasChild)) && this.fields.groupBy) {
1467
+ curDS = <DataSource[]>this.curViewDS;
1468
+ } else {
1469
+ curDS = <DataSource[]>dataSource;
1470
+ }
1471
+
1472
+ return <DataSource>this.findItemFromDS(curDS, <DataSource>fields);
1473
+ }
1474
+
1475
+ private findItemFromDS(
1476
+ dataSource: DataSource[],
1477
+ fields: DataSource,
1478
+ parent?: boolean): DataSource[] | DataSource {
1479
+
1480
+ let resultJSON: DataSource[] | DataSource;
1481
+
1482
+ if (dataSource && dataSource.length && fields) {
1483
+ dataSource.some((data: DataSource) => {
1484
+ const fieldData: DataSource =
1485
+ // eslint-disable-next-line
1486
+ <DataSource>getFieldValues(data as { [key: string]: Object } as any, this.listBaseOption.fields);
1487
+ //(!(fid) || id === fid) && (!(ftext) || text === ftext) && (!!fid || !!ftext)
1488
+ if ((fields[this.fields.id] || fields[this.fields.text]) &&
1489
+ (!fields[this.fields.id] || (!isNullOrUndefined(fieldData[this.fields.id]) &&
1490
+ fieldData[this.fields.id].toString()) === fields[this.fields.id].toString()) &&
1491
+ (!fields[this.fields.text] || fieldData[this.fields.text] === fields[this.fields.text])) {
1492
+ resultJSON = (parent ? <DataSource[] | DataSource>dataSource : data);
1493
+ } else if (typeof data !== 'object' && dataSource.indexOf(data) !== -1) {
1494
+ resultJSON = (parent ? <DataSource[] | DataSource>dataSource : data);
1495
+ } else if (!isNullOrUndefined(fields[this.fields.id]) && isNullOrUndefined(fieldData[this.fields.id])) {
1496
+ const li: Element = this.element.querySelector('[data-uid="'
1497
+ + fields[this.fields.id] + '"]');
1498
+ // eslint-disable-next-line
1499
+ if (li && (li as HTMLElement).innerText.trim() === fieldData[this.fields.text] as any) {
1500
+ resultJSON = data;
1501
+ }
1502
+ // eslint-disable-next-line
1503
+ } else if ((fieldData as object).hasOwnProperty(this.fields.child) && (fieldData[this.fields.child] as object[]).length) {
1504
+ resultJSON = <DataSource & DataSource[]>this.findItemFromDS(
1505
+ <DataSource[]>fieldData[this.fields.child], fields, parent);
1506
+ }
1507
+ return !!resultJSON;
1508
+ });
1509
+ } else {
1510
+ resultJSON = <DataSource[] & DataSource>dataSource;
1511
+ }
1512
+
1513
+ return <DataSource[] & DataSource>resultJSON;
1514
+ }
1515
+
1516
+ private getQuery(): Query {
1517
+ const columns: string[] = [];
1518
+ const query: Query = (this.query ? this.query : new Query());
1519
+ if (!this.query) {
1520
+ // eslint-disable-next-line
1521
+ for (const column of Object.keys((this.fields as any & { properties: object }).properties)) {
1522
+ if (column !== 'tableName' && !!((this.fields as DataSource)[column]) &&
1523
+ (this.fields as DataSource)[column] !==
1524
+ (ListBase.defaultMappedFields as DataSource)[column]
1525
+ && columns.indexOf((this.fields as { [key: string]: string })[column]) === -1) {
1526
+
1527
+ columns.push((this.fields as { [key: string]: string })[column]);
1528
+
1529
+ }
1530
+ }
1531
+ query.select(columns);
1532
+ // eslint-disable-next-line
1533
+ if ((this.fields as any & { properties: object }).properties.hasOwnProperty('tableName')) {
1534
+ query.from(this.fields.tableName);
1535
+ }
1536
+ }
1537
+
1538
+ return query;
1539
+ }
1540
+
1541
+ private setViewDataSource(dataSource: DataSource[] = <DataSource[]>this.localData): void {
1542
+ // eslint-disable-next-line
1543
+ let fieldValue: any = (isNullOrUndefined(this.fields.sortBy)) ? this.fields.text : this.fields.sortBy;
1544
+ let query: Query = ListBase.addSorting(this.sortOrder, fieldValue);
1545
+ if (dataSource && this.fields.groupBy) {
1546
+ if (this.sortOrder !== 'None') {
1547
+ this.curViewDS = ListBase.groupDataSource(
1548
+ ListBase.getDataSource(dataSource, query),
1549
+ this.listBaseOption.fields, this.sortOrder);
1550
+ } else {
1551
+ this.curViewDS = ListBase.groupDataSource(dataSource, this.listBaseOption.fields, this.sortOrder);
1552
+ }
1553
+ } else if (dataSource && this.sortOrder !== 'None') {
1554
+ this.curViewDS = ListBase.getDataSource(dataSource, query);
1555
+ } else {
1556
+ this.curViewDS = dataSource;
1557
+ }
1558
+ }
1559
+
1560
+ private isInAnimation(): boolean {
1561
+ return this.curUL.classList.contains('.e-animate');
1562
+ }
1563
+
1564
+ private renderRemoteLists(e: Object, listViewComponent: ListView): void {
1565
+ if (this.isDestroyed) { return; }
1566
+ this.localData = (e as ResultData).result;
1567
+ listViewComponent.removeElement(listViewComponent.contentContainer);
1568
+ this.renderList();
1569
+ this.trigger('actionComplete', e);
1570
+ }
1571
+
1572
+ private triggerActionFailure(e: Object): void {
1573
+ if (this.isDestroyed) { return; }
1574
+ this.trigger('actionFailure', e);
1575
+ }
1576
+
1577
+ private setLocalData(): void {
1578
+ this.trigger('actionBegin');
1579
+ // eslint-disable-next-line
1580
+ const listViewComponent: ListView = this;
1581
+ if (this.dataSource instanceof DataManager) {
1582
+ if ((this.dataSource as DataManager).ready) {
1583
+ (this.dataSource as DataManager).ready.then((e: Object) => {
1584
+ /* eslint-disable */
1585
+ this.isOffline = (this.dataSource as DataManager).dataSource.offline;
1586
+ if (this.dataSource instanceof DataManager && this.isOffline) {
1587
+ this.renderRemoteLists(e, listViewComponent);
1588
+ }
1589
+ }).catch((e: Object) => {
1590
+ this.triggerActionFailure(e);
1591
+ });
1592
+ } else {
1593
+ (this.dataSource as DataManager).executeQuery(this.getQuery()).then((e: object) => {
1594
+ this.renderRemoteLists(e, listViewComponent);
1595
+ }).catch((e: Object) => {
1596
+ this.triggerActionFailure(e);
1597
+ });
1598
+ }
1599
+ } else if (!this.dataSource || !(<DataSource[]>this.dataSource).length) {
1600
+ const ul: HTMLElement = <HTMLElement>this.element.querySelector('ul');
1601
+ if (ul) {
1602
+ remove(ul);
1603
+ this.setProperties({ dataSource: ListBase.createJsonFromElement(ul) }, true);
1604
+ this.localData = <DataSource[]>this.dataSource;
1605
+ this.renderList();
1606
+ this.trigger('actionComplete', { data: this.localData });
1607
+ }
1608
+ } else {
1609
+ this.localData = <DataSource[]>this.dataSource;
1610
+ this.renderList();
1611
+ this.trigger('actionComplete', { data: this.localData });
1612
+ }
1613
+ }
1614
+
1615
+ private reRender(): void {
1616
+ this.removeElement(this.headerEle);
1617
+ this.removeElement(this.ulElement);
1618
+ this.removeElement(this.contentContainer);
1619
+ // eslint-disable-next-line
1620
+ if ((this as any).isReact) {
1621
+ this.clearTemplate();
1622
+ }
1623
+ if (Object.keys(window).indexOf('ejsInterop') === -1) {
1624
+ this.element.innerHTML = '';
1625
+ }
1626
+ this.headerEle = this.ulElement = this.liCollection = undefined;
1627
+ this.header();
1628
+ this.setLocalData();
1629
+ }
1630
+
1631
+ private resetCurrentList(): void {
1632
+ this.setViewDataSource(this.curViewDS as DataSource[]);
1633
+ this.contentContainer.innerHTML = '';
1634
+ this.createList();
1635
+ this.renderIntoDom(this.curUL);
1636
+ }
1637
+
1638
+ private setAttributes(liElements: HTMLElement[]): void {
1639
+ for (let i: number = 0; i < liElements.length; i++) {
1640
+ const element: HTMLElement = liElements[i];
1641
+ if (element.classList.contains('e-list-item')) {
1642
+ element.setAttribute('id', this.element.id + '_' + element.getAttribute('data-uid'));
1643
+ element.setAttribute('aria-selected', 'false');
1644
+ element.setAttribute('tabindex', '-1');
1645
+ }
1646
+ }
1647
+ }
1648
+
1649
+ private createList(): void {
1650
+ this.currentLiElements = [];
1651
+ this.isNestedList = false;
1652
+ this.ulElement = this.curUL = ListBase.createList(
1653
+ this.createElement, this.curViewDS as DataSource[], this.listBaseOption, null, this);
1654
+ this.liCollection = <HTMLElement[] & NodeListOf<Element>>this.curUL.querySelectorAll('.' + classNames.listItem);
1655
+ this.setAttributes(this.liCollection);
1656
+ }
1657
+
1658
+ private exceptionEvent(e: object): void {
1659
+ this.trigger('actionFailure', e);
1660
+ }
1661
+ private UpdateCurrentUL(): void {
1662
+ this.ulElement = this.curUL = this.element.querySelector('.' + classNames.parentItem);
1663
+ if (this.curUL) {
1664
+ // eslint-disable-next-line
1665
+ (this as any).liCollection = this.curUL.querySelectorAll('.' + classNames.listItem);
1666
+ }
1667
+ }
1668
+
1669
+ private renderSubList(li: Element): void {
1670
+ this.liElement = li;
1671
+ const uID: string = li.getAttribute('data-uid');
1672
+ if (li.classList.contains(classNames.hasChild) && uID) {
1673
+ const ul: Element = closest(li.parentNode, '.' + classNames.parentItem);
1674
+ let ele: Element = this.element.querySelector('[pid=\'' + uID + '\']');
1675
+ this.curDSLevel.push(uID);
1676
+ this.setViewDataSource(this.getSubDS());
1677
+ if (!ele) {
1678
+ const data: DataSource[] = this.curViewDS as DataSource[];
1679
+ ele = ListBase.createListFromJson(this.createElement, data, this.listBaseOption, this.curDSLevel.length, null, this);
1680
+ // eslint-disable-next-line
1681
+ if ((this as any).isReact) {
1682
+ this.renderReactTemplates();
1683
+ }
1684
+ const lists: HTMLElement[] = <HTMLElement[] & NodeListOf<Element>>ele.querySelectorAll('.' + classNames.listItem);
1685
+ this.setAttributes(lists);
1686
+ ele.setAttribute('pID', <string>uID);
1687
+ (ele as HTMLElement).style.display = 'none';
1688
+ this.renderIntoDom(ele);
1689
+ }
1690
+ this.switchView(<HTMLElement>ul, <HTMLElement>ele);
1691
+ this.liCollection = <HTMLElement[] & NodeListOf<Element>>this.curUL.querySelectorAll('.' + classNames.listItem);
1692
+ if (this.selectedItems) {
1693
+ const fieldData: DataSource = <DataSource>
1694
+ getFieldValues(<DataSource | string>this.selectedItems.data, this.listBaseOption.fields);
1695
+
1696
+ // eslint-disable-next-line
1697
+ this.header((fieldData[this.listBaseOption.fields.text]) as any, true, 'header');
1698
+ }
1699
+ this.selectedLI = undefined;
1700
+ }
1701
+ }
1702
+
1703
+ private renderIntoDom(ele: Element): void {
1704
+ this.contentContainer.appendChild(ele);
1705
+ }
1706
+
1707
+ private renderList(data?: DataSource[]): void {
1708
+ this.setViewDataSource(data);
1709
+ if (this.enableVirtualization) {
1710
+ if (Object.keys(this.dataSource).length) {
1711
+ if ((this.template || this.groupTemplate) && !this.virtualizationModule.isNgTemplate()) {
1712
+ if ((this as any).isReact) {
1713
+ if (typeof this.template == "string") {
1714
+ this.listBaseOption.template = null;
1715
+ }
1716
+ if (typeof this.groupTemplate == "string") {
1717
+ this.listBaseOption.groupTemplate = null;
1718
+ }
1719
+ } else {
1720
+ this.listBaseOption.template = null;
1721
+ this.listBaseOption.groupTemplate = null;
1722
+ }
1723
+ this.listBaseOption.itemCreated = this.virtualizationModule.createUIItem.bind(this.virtualizationModule);
1724
+ }
1725
+ }
1726
+ this.virtualizationModule.uiVirtualization();
1727
+ } else {
1728
+ this.createList();
1729
+ this.contentContainer = this.createElement('div', { className: classNames.content });
1730
+ this.element.appendChild(this.contentContainer);
1731
+ this.renderIntoDom(this.ulElement);
1732
+ // eslint-disable-next-line
1733
+ if ((this as any).isReact) {
1734
+ this.renderReactTemplates();
1735
+ }
1736
+ }
1737
+ }
1738
+
1739
+ private getElementUID(obj: Fields | HTMLElement | Element): Fields {
1740
+ let fields: DataSource = {};
1741
+ if (obj instanceof Element) {
1742
+ // eslint-disable-next-line
1743
+ fields[this.fields.id] = obj.getAttribute('data-uid') as any;
1744
+ } else {
1745
+ fields = <DataSource>obj;
1746
+ }
1747
+ return fields;
1748
+ }
1749
+
1750
+ /**
1751
+ * Initializes the ListView component rendering.
1752
+ */
1753
+
1754
+ public render(): void {
1755
+ this.element.classList.add(classNames.root);
1756
+ attributes(this.element, { role: 'list', tabindex: '0' });
1757
+ this.setCSSClass();
1758
+ this.setEnableRTL();
1759
+ this.setEnable();
1760
+ this.setSize();
1761
+ this.wireEvents();
1762
+ this.header();
1763
+ this.setLocalData();
1764
+ this.setHTMLAttribute();
1765
+ this.rippleFn = rippleEffect(this.element, {
1766
+ selector: '.' + classNames.listItem
1767
+ });
1768
+ this.renderComplete();
1769
+ }
1770
+
1771
+ /**
1772
+ * It is used to destroy the ListView component.
1773
+ */
1774
+
1775
+ public destroy(): void {
1776
+ // eslint-disable-next-line
1777
+ if ((this as any).isReact) {
1778
+ this.clearTemplate();
1779
+ }
1780
+ this.unWireEvents();
1781
+ const classAr: string[] = [classNames.root, classNames.disable, 'e-rtl',
1782
+ 'e-has-header', 'e-lib'].concat(this.cssClass.split(' ').filter((css: string) => css));
1783
+ removeClass([this.element], classAr);
1784
+ this.element.removeAttribute('role');
1785
+ this.element.removeAttribute('tabindex');
1786
+ this.curUL = this.ulElement = this.liCollection = this.headerEle = undefined;
1787
+ this.element.innerHTML = '';
1788
+ super.destroy();
1789
+ }
1790
+
1791
+ /**
1792
+ * Switches back from the navigated sub list item.
1793
+ */
1794
+
1795
+ public back(): void {
1796
+ const pID: string = this.curDSLevel[this.curDSLevel.length - 1];
1797
+ if (pID === undefined || this.isInAnimation()) { return; }
1798
+ this.curDSLevel.pop();
1799
+ this.setViewDataSource(this.getSubDS());
1800
+ let toUL: HTMLElement = <HTMLElement>this.element.querySelector('[data-uid=\'' + pID + '\']');
1801
+ const fromUL: HTMLElement = this.curUL;
1802
+ if (!toUL) {
1803
+ this.createList();
1804
+ this.renderIntoDom(this.ulElement);
1805
+ toUL = this.curUL;
1806
+ } else {
1807
+ toUL = toUL.parentElement;
1808
+ }
1809
+ const fieldData: DataSource = <DataSource>getFieldValues(this.curDSJSON, this.listBaseOption.fields);
1810
+ // eslint-disable-next-line
1811
+ const text: string = fieldData[this.fields.text] as any;
1812
+ this.switchView(fromUL, toUL, true);
1813
+ this.removeFocus();
1814
+ const li: HTMLElement = <HTMLElement>this.element.querySelector('[data-uid=\'' + pID + '\']');
1815
+ li.classList.remove(classNames.disable);
1816
+ li.classList.add(classNames.focused);
1817
+ if (this.showCheckBox && li.querySelector('.' + classNames.checkboxIcon).classList.contains(classNames.checked)) {
1818
+ li.setAttribute('aria-selected', 'true');
1819
+ } else {
1820
+ li.classList.remove(classNames.selected);
1821
+ li.setAttribute('aria-selected', 'false');
1822
+ }
1823
+ this.liCollection = <HTMLElement[] & NodeListOf<Element>>this.curUL.querySelectorAll('.' + classNames.listItem);
1824
+ if (this.enableHtmlSanitizer) {
1825
+ this.setProperties({ headerTitle: SanitizeHtmlHelper.sanitize(this.headerTitle) }, true);
1826
+ }
1827
+ this.header((this.curDSLevel.length ? text : this.headerTitle), (this.curDSLevel.length ? true : false), 'header');
1828
+ }
1829
+
1830
+ /**
1831
+ * Selects the list item from the ListView by passing the elements or field object.
1832
+ *
1833
+ * @param {Fields | HTMLElement | Element} item - We can pass element Object or Fields as Object with ID and Text fields.
1834
+ */
1835
+
1836
+ public selectItem(item: Fields | HTMLElement | Element): void {
1837
+ if (this.enableVirtualization) {
1838
+ this.virtualizationModule.selectItem(item);
1839
+ } else if (this.showCheckBox) {
1840
+ this.setCheckboxLI(this.getLiFromObjOrElement(item));
1841
+ } else {
1842
+ if (isNullOrUndefined(item) === true) {
1843
+ this.removeSelect();
1844
+ }
1845
+ else {
1846
+ this.setSelectLI(this.getLiFromObjOrElement(item));
1847
+ }
1848
+
1849
+ }
1850
+ }
1851
+
1852
+ private getLiFromObjOrElement(obj: Fields | HTMLElement | Element): HTMLElement {
1853
+ let li: Element;
1854
+ const dataSource: DataSource[] | string[] | number[] | DataManager = this.dataSource instanceof DataManager ?
1855
+ this.localData : this.dataSource;
1856
+ if (!isNullOrUndefined(obj)) {
1857
+ if (typeof (dataSource as string[])[0] === 'string' || typeof (dataSource as number[])[0] === 'number') {
1858
+ if (obj instanceof Element) {
1859
+ const uid: string = (obj as HTMLElement).getAttribute('data-uid').toString();
1860
+ for (let i: number = 0; i < this.liCollection.length; i++) {
1861
+ if (this.liCollection[i].getAttribute('data-uid').toString() === uid) {
1862
+ li = this.liCollection[i] as HTMLElement;
1863
+ break;
1864
+ }
1865
+ }
1866
+ } else {
1867
+ Array.prototype.some.call(this.curUL.querySelectorAll('.' + classNames.listItem), (item: HTMLElement) => {
1868
+ if (item.innerText.trim() === obj.toString()) {
1869
+ li = item;
1870
+ return true;
1871
+ } else {
1872
+ return false;
1873
+ }
1874
+ });
1875
+ }
1876
+ } else {
1877
+ const resultJSON: DataSource[] | DataSource = this.getItemData(obj);
1878
+ const fieldData: DataSource =
1879
+ <DataSource>getFieldValues(resultJSON, this.listBaseOption.fields);
1880
+ if (resultJSON) {
1881
+ li = this.element.querySelector('[data-uid="'
1882
+ + (<DataSource>fieldData)[this.fields.id] + '"]');
1883
+ if (!this.enableVirtualization && isNullOrUndefined(li)) {
1884
+ const curLi: NodeListOf<Element> = this.element.querySelectorAll('.' + classNames.listItem);
1885
+ for (let i: number = 0; i < curLi.length; i++) {
1886
+ // eslint-disable-next-line
1887
+ if ((curLi[i] as HTMLElement).innerText.trim() === fieldData[this.fields.text] as any) {
1888
+ li = curLi[i];
1889
+ }
1890
+ }
1891
+ }
1892
+ }
1893
+ }
1894
+ }
1895
+ return <HTMLElement>li;
1896
+ }
1897
+
1898
+ /**
1899
+ * Selects multiple list items from the ListView.
1900
+ *
1901
+ * @param {Fields[] | HTMLElement[] | Element[]} item - We can pass array of
1902
+ * elements or array of fields Object with ID and Text fields.
1903
+ */
1904
+
1905
+ public selectMultipleItems(item: Fields[] | HTMLElement[] | Element[]): void {
1906
+ if (!isNullOrUndefined(item)) {
1907
+ for (let i: number = 0; i < item.length; i++) {
1908
+ if (!isNullOrUndefined(item[i])) {
1909
+ this.selectItem(item[i]);
1910
+ }
1911
+ }
1912
+ }
1913
+ }
1914
+
1915
+ private getParentId(): string[] {
1916
+ const parentId: string[] = [];
1917
+ if (this.isNestedList) {
1918
+ for (let i: number = this.curDSLevel.length - 1; i >= 0; i--) {
1919
+ parentId.push(this.curDSLevel[i]);
1920
+ }
1921
+ }
1922
+ return parentId;
1923
+ }
1924
+
1925
+ /**
1926
+ * Gets the details of the currently selected item from the list items.
1927
+ *
1928
+ */
1929
+
1930
+ public getSelectedItems(): SelectedItem | SelectedCollection | UISelectedItem | NestedListData {
1931
+ // eslint-disable-next-line
1932
+ let finalValue: any;
1933
+ let isCompleted: boolean = false;
1934
+ this.selectedId = [];
1935
+ const dataSource: DataSource[] | string[] | number[] | DataManager = this.dataSource instanceof DataManager ?
1936
+ this.localData : this.dataSource;
1937
+ if (this.enableVirtualization && !isCompleted) {
1938
+ finalValue = this.virtualizationModule.getSelectedItems();
1939
+ isCompleted = true;
1940
+ } else if (this.showCheckBox && !isCompleted) {
1941
+ // eslint-disable-next-line
1942
+ const liCollection: any = this.curUL.getElementsByClassName(classNames.selected);
1943
+ const liTextCollection: string[] = []; const liDataCollection: DataSource[] = []; this.selectedId = [];
1944
+ const dataParent: DataAndParent[] = [];
1945
+ for (let i: number = 0; i < liCollection.length; i++) {
1946
+ if (typeof (dataSource as string[])[0] === 'string' || typeof (dataSource as number[])[0] === 'number') {
1947
+ liTextCollection.push((liCollection[i] as HTMLElement).innerText.trim());
1948
+ } else {
1949
+ const tempData: DataSource = this.getItemData(liCollection[i] as HTMLElement);
1950
+ const fieldData: DataSource =
1951
+ <DataSource>getFieldValues(tempData, this.listBaseOption.fields);
1952
+ if (this.isNestedList) {
1953
+ dataParent.push({ data: tempData, parentId: this.getParentId() });
1954
+ } else {
1955
+ liDataCollection.push(tempData);
1956
+ }
1957
+ if (fieldData) {
1958
+ // eslint-disable-next-line
1959
+ liTextCollection.push(fieldData[this.listBaseOption.fields.text] as any);
1960
+ // eslint-disable-next-line
1961
+ this.selectedId.push(fieldData[this.listBaseOption.fields.id] as any);
1962
+ } else {
1963
+ liTextCollection.push(undefined);
1964
+ this.selectedId.push(undefined);
1965
+ }
1966
+ }
1967
+ }
1968
+ if ((typeof (dataSource as string[])[0] === 'string'
1969
+ || typeof (dataSource as number[])[0] === 'number')
1970
+ && !isCompleted) {
1971
+ finalValue = { item: liCollection, data: dataSource as string[] | number[], text: liTextCollection };
1972
+ isCompleted = true;
1973
+ }
1974
+ if (this.isNestedList && !isCompleted) {
1975
+ finalValue = { item: liCollection, data: dataParent, text: liTextCollection };
1976
+ isCompleted = true;
1977
+ } else if (!isCompleted) {
1978
+ finalValue = { item: liCollection, data: liDataCollection, text: liTextCollection };
1979
+ isCompleted = true;
1980
+ }
1981
+ } else if (!isCompleted) {
1982
+ const liElement: Element = this.element.getElementsByClassName(classNames.selected)[0];
1983
+ const fieldData: DataSource =
1984
+ <DataSource>getFieldValues(this.getItemData(liElement), this.listBaseOption.fields);
1985
+ if ((typeof (dataSource as string[])[0] === 'string'
1986
+ || typeof (dataSource as number[])[0] === 'number')
1987
+ && !isCompleted) {
1988
+ finalValue = (!isNullOrUndefined(liElement)) ? {
1989
+ item: liElement, data: dataSource as string[] | number[],
1990
+ text: (liElement as HTMLElement).innerText.trim()
1991
+ } : undefined;
1992
+ isCompleted = true;
1993
+ } else if (!isCompleted) {
1994
+ if (isNullOrUndefined(fieldData) || isNullOrUndefined(liElement)) {
1995
+ finalValue = undefined;
1996
+ isCompleted = true;
1997
+ } else {
1998
+ // eslint-disable-next-line
1999
+ this.selectedId.push(fieldData[this.listBaseOption.fields.id] as any);
2000
+ finalValue = {
2001
+ text: fieldData[this.listBaseOption.fields.text], item: liElement,
2002
+ data: this.getItemData(liElement)
2003
+ };
2004
+ isCompleted = true;
2005
+ }
2006
+ }
2007
+ }
2008
+ return finalValue;
2009
+ }
2010
+
2011
+ /**
2012
+ * Finds out an item details from the current list.
2013
+ *
2014
+ * @param {Fields | HTMLElement | Element} item - We can pass element Object or Fields as Object with ID and Text fields.
2015
+ */
2016
+
2017
+ public findItem(item: Fields | HTMLElement | Element): SelectedItem {
2018
+ return <SelectedItem & DataSource>this.getItemData(item);
2019
+ }
2020
+
2021
+ /**
2022
+ * Enables the disabled list items by passing the Id and text fields.
2023
+ *
2024
+ * @param {Fields | HTMLElement | Element} item - We can pass element Object or Fields as Object with ID and Text fields.
2025
+ */
2026
+
2027
+ public enableItem(item: Fields | HTMLElement | Element): void {
2028
+ this.setItemState(item, true);
2029
+ if (this.enableVirtualization) {
2030
+ this.virtualizationModule.enableItem(item);
2031
+ }
2032
+ }
2033
+
2034
+ /**
2035
+ * Disables the list items by passing the Id and text fields.
2036
+ *
2037
+ * @param {Fields | HTMLElement | Element} item - We can pass element Object or Fields as Object with ID and Text fields.
2038
+ */
2039
+
2040
+ public disableItem(item: Fields | HTMLElement | Element): void {
2041
+ this.setItemState(item, false);
2042
+ if (this.enableVirtualization) {
2043
+ this.virtualizationModule.disableItem(item);
2044
+ }
2045
+ }
2046
+
2047
+ //A function that used to set state of the list item like enable, disable.
2048
+ private setItemState(item: Fields | HTMLElement | Element, isEnable: boolean): void {
2049
+ const resultJSON: DataSource = this.getItemData(item);
2050
+ const fieldData: DataSource = <DataSource>getFieldValues(resultJSON, this.listBaseOption.fields);
2051
+ if (resultJSON) {
2052
+ const li: Element = this.element.querySelector('[data-uid="' + fieldData[this.fields.id] + '"]');
2053
+ if (isEnable) {
2054
+ if (li) { li.classList.remove(classNames.disable); }
2055
+ delete resultJSON[this.fields.enabled];
2056
+ } else if (!isEnable) {
2057
+ if (li) { li.classList.add(classNames.disable); }
2058
+ // eslint-disable-next-line
2059
+ resultJSON[this.fields.enabled] = false as any;
2060
+ }
2061
+ }
2062
+ }
2063
+
2064
+ /**
2065
+ * Shows the hide list item from the ListView.
2066
+ *
2067
+ * @param {Fields | HTMLElement | Element} item - We can pass element Object or Fields as Object with ID and Text fields.
2068
+ */
2069
+
2070
+ public showItem(item: Fields | HTMLElement | Element): void {
2071
+ this.showHideItem(item, false, '');
2072
+ if (this.enableVirtualization) {
2073
+ this.virtualizationModule.showItem(item);
2074
+ }
2075
+ }
2076
+
2077
+ /**
2078
+ * Hides an list item from the ListView.
2079
+ *
2080
+ * @param {Fields | HTMLElement | Element} item - We can pass element Object or Fields as Object with ID and Text fields.
2081
+ */
2082
+
2083
+ public hideItem(item: Fields | HTMLElement | Element): void {
2084
+ this.showHideItem(item, true, 'none');
2085
+ if (this.enableVirtualization) {
2086
+ this.virtualizationModule.hideItem(item);
2087
+ }
2088
+ }
2089
+
2090
+ private showHideItem(obj: Fields | HTMLElement | Element, isHide: boolean, display: string): void {
2091
+ const resultJSON: DataSource = this.getItemData(obj);
2092
+ const fieldData: DataSource = <DataSource>getFieldValues(resultJSON, this.listBaseOption.fields);
2093
+ if (resultJSON) {
2094
+ const li: HTMLElement = <HTMLElement>this.element.querySelector('[data-uid="' + fieldData[this.fields.id] + '"]');
2095
+ if (li) { li.style.display = display; }
2096
+ if (isHide) {
2097
+ // eslint-disable-next-line
2098
+ resultJSON[this.fields.isVisible] = false as any;
2099
+ } else {
2100
+ delete resultJSON[this.fields.isVisible];
2101
+ }
2102
+ }
2103
+ }
2104
+
2105
+ /**
2106
+ * Adds the new list item(s) to the current ListView.
2107
+ * To add a new list item(s) in the ListView, we need to pass the `data` as an array of items that need
2108
+ * to be added and `fields` as the target item to which we need to add the given item(s) as its children.
2109
+ * For example fields: { text: 'Name', tooltip: 'Name', id:'id'}
2110
+ *
2111
+ * @param {{Object}[]} data - JSON Array Data that need to add.
2112
+ *
2113
+ * @param {Fields} fields - Target item to add the given data as its children (can be null).
2114
+ *
2115
+ */
2116
+
2117
+ public addItem(data: { [key: string]: Object }[], fields: Fields = undefined): void {
2118
+ const dataSource: DataSource[] = this.dataSource instanceof DataManager
2119
+ ? this.localData : this.dataSource as DataSource[];
2120
+ this.addItemInternally(data, fields, dataSource);
2121
+ }
2122
+
2123
+ private addItemInternally(data: DataSource[], fields: Fields, dataSource: DataSource[]): void {
2124
+ if (data instanceof Array) {
2125
+ if (this.enableVirtualization) {
2126
+ this.virtualizationModule.addItem(data, fields, dataSource);
2127
+ } else {
2128
+ const ds: DataSource = <DataSource>this.findItemFromDS(
2129
+ <DataSource[]>dataSource, <DataSource>fields);
2130
+ let child: DataSource[];
2131
+ if (ds) {
2132
+ const fieldData: DataSource = <DataSource>getFieldValues(ds, this.listBaseOption.fields);
2133
+ child = <DataSource[]>fieldData[this.fields.child];
2134
+ if (!child) {
2135
+ child = [];
2136
+ }
2137
+ child = child.concat(data);
2138
+ }
2139
+ // check for whether target is nested level or top level in list
2140
+ if (ds instanceof Array) {
2141
+ for (let i: number = 0; i < data.length; i++) {
2142
+ dataSource.push(data[i]);
2143
+ this.setViewDataSource(dataSource);
2144
+ // since it is top level target, get the content container's first child
2145
+ // as it is always the top level UL
2146
+ const targetUL: HTMLElement = this.contentContainer
2147
+ ? <HTMLElement>this.contentContainer.children[0]
2148
+ : null;
2149
+ // check for whether the list was previously empty or not, if it is
2150
+ // proceed to call initial render
2151
+ if (this.contentContainer && targetUL) {
2152
+ this.addItemIntoDom(data[i], targetUL, this.curViewDS as DataSource[]);
2153
+ } else {
2154
+ this.reRender();
2155
+ }
2156
+ }
2157
+ this.liCollection = <HTMLElement[] & NodeListOf<Element>>this.curUL.querySelectorAll('.' + classNames.listItem);
2158
+ } else {
2159
+ // proceed as target item is in nested level, only if it is a valid target ds
2160
+ if (ds) {
2161
+ ds[this.fields.child] = child;
2162
+ this.addItemInNestedList(ds, data);
2163
+ }
2164
+ }
2165
+ }
2166
+ }
2167
+ }
2168
+
2169
+ private addItemInNestedList(targetItemData: DataSource, itemQueue: DataSource[]): void {
2170
+ // eslint-disable-next-line
2171
+ const targetItemId: string = targetItemData[this.fields.id] as any;
2172
+ const targetChildDS: DataSource[] = targetItemData[this.fields.child] as DataSource[];
2173
+ const isAlreadyRenderedUL: HTMLElement | null = this.element.querySelector('[pid=\'' + targetItemId + '\']');
2174
+ const targetLi: HTMLElement | null = this.element.querySelector('[data-uid=\'' + targetItemId + '\']');
2175
+ const targetUL: HTMLElement | null = isAlreadyRenderedUL
2176
+ ? isAlreadyRenderedUL
2177
+ : targetLi
2178
+ ? <HTMLElement>closest(targetLi, 'ul')
2179
+ : null;
2180
+ const targetDS: DataSource[] = isAlreadyRenderedUL ? targetChildDS : [targetItemData];
2181
+ const isTargetEmptyChild: boolean = targetLi ? !targetLi.classList.contains(classNames.hasChild) : false;
2182
+ let isRefreshTemplateNeeded: boolean = false;
2183
+ // if li element is already rendered, that element needs to be refreshed so that
2184
+ // it becomes child viewable due to new child items are added now
2185
+ if (isTargetEmptyChild) {
2186
+ const targetRefreshedElement: HTMLElement[] = ListBase.createListItemFromJson(
2187
+ this.createElement, targetDS, this.listBaseOption, null, null, this);
2188
+ this.setAttributes(targetRefreshedElement);
2189
+ targetUL.insertBefore(targetRefreshedElement[0], targetLi);
2190
+ detach(targetLi);
2191
+ isRefreshTemplateNeeded = true;
2192
+ }
2193
+ // if it is already rendered element, we need to create and append new elements
2194
+ if (isAlreadyRenderedUL && itemQueue) {
2195
+ for (let i: number = 0; i < itemQueue.length; i++) {
2196
+ targetDS.push(itemQueue[i]);
2197
+ this.addItemIntoDom(itemQueue[i], targetUL, targetDS);
2198
+ }
2199
+ isRefreshTemplateNeeded = true;
2200
+ }
2201
+ }
2202
+
2203
+ private addItemIntoDom(currentItem: DataSource, targetUL: HTMLElement, curViewDS: DataSource[]): void {
2204
+ const index: number = curViewDS.indexOf(currentItem);
2205
+ this.addListItem(currentItem, index, targetUL, curViewDS);
2206
+ const curItemDS: DataSource = curViewDS[index - 1];
2207
+ if (curItemDS && curItemDS.isHeader && (curItemDS.items as DataSource[]).length === 1) {
2208
+ this.addListItem(curItemDS, (index - 1), targetUL, curViewDS);
2209
+ }
2210
+ }
2211
+
2212
+ private addListItem(dataSource: DataSource, index: number, ulElement: HTMLElement, curViewDS: DataSource[]): void {
2213
+ let target: HTMLElement = this.getLiFromObjOrElement((curViewDS as DataSource[])[index + 1]) ||
2214
+ this.getLiFromObjOrElement((curViewDS as DataSource[])[index + 2]) || null;
2215
+ const li: HTMLElement[] = ListBase.createListItemFromJson(this.createElement, [dataSource], this.listBaseOption, null, null, this);
2216
+ this.setAttributes(li);
2217
+ // eslint-disable-next-line
2218
+ if (this.template && (this as any).isReact) {
2219
+ this.renderReactTemplates();
2220
+ }
2221
+ if (this.fields.groupBy && curViewDS[index + 1] && curViewDS[index + 1].isHeader) {
2222
+ let targetEle: HTMLElement = this.getLiFromObjOrElement(curViewDS[index - 1]);
2223
+ if (targetEle) {
2224
+ target = targetEle.nextElementSibling as HTMLElement;
2225
+ }
2226
+ }
2227
+ ulElement.insertBefore(li[0], target);
2228
+ }
2229
+
2230
+ /**
2231
+ * Removes the list item from the data source based on a passed
2232
+ * element like fields: { text: 'Name', tooltip: 'Name', id:'id'}
2233
+ *
2234
+ * @param {Fields | HTMLElement | Element} item - We can pass element Object or Fields as Object with ID and Text fields.
2235
+ */
2236
+
2237
+ public removeItem(item: Fields | HTMLElement | Element): void {
2238
+ const listDataSource: DataSource[] = this.dataSource instanceof DataManager
2239
+ ? this.localData : this.dataSource as DataSource[];
2240
+ if (this.enableVirtualization) {
2241
+ this.virtualizationModule.removeItem(item);
2242
+ } else {
2243
+ this.removeItemFromList(item, listDataSource);
2244
+ }
2245
+ }
2246
+
2247
+ private removeItemFromList(obj: Fields | Element | HTMLElement, listDataSource: DataSource[]): void {
2248
+ const curViewDS: DataSource[] = this.curViewDS as DataSource[];
2249
+ const fields: Fields = obj instanceof Element ? this.getElementUID(obj) : obj;
2250
+ let dataSource: DataSource[] | DataSource;
2251
+ // eslint-disable-next-line prefer-const
2252
+ dataSource = this.findItemFromDS(listDataSource, <DataSource>fields, true);
2253
+ if (dataSource) {
2254
+ let data: DataSource | DataSource[];
2255
+ // eslint-disable-next-line prefer-const
2256
+ data = this.findItemFromDS(dataSource as DataSource[], <DataSource>fields);
2257
+ const index: number = curViewDS.indexOf(data as DataSource);
2258
+ const li: HTMLElement = this.getLiFromObjOrElement(obj);
2259
+ let groupLi: HTMLElement;
2260
+ this.validateNestedView(li);
2261
+ if (this.fields.groupBy && this.curViewDS[index - 1] &&
2262
+ curViewDS[index - 1].isHeader &&
2263
+ (curViewDS[index - 1].items as DataSource[]).length === 1) {
2264
+ if (li && (li.previousElementSibling as HTMLElement).classList.contains(classNames.groupListItem) &&
2265
+ (isNullOrUndefined(li.nextElementSibling) || (li.nextElementSibling &&
2266
+ (li.nextElementSibling as HTMLElement).classList.contains(classNames.groupListItem)))) {
2267
+ groupLi = li.previousElementSibling as HTMLElement;
2268
+ }
2269
+ }
2270
+ if (li) {
2271
+ detach(li);
2272
+ }
2273
+ if (groupLi) {
2274
+ detach(groupLi);
2275
+ }
2276
+ // eslint-disable-next-line
2277
+ const foundData: any = ((<DataSource[]>dataSource).length - 1) <= 0
2278
+ ? this.findParent(
2279
+ this.localData,
2280
+ this.fields.id,
2281
+ // eslint-disable-next-line
2282
+ (value: string) => value === (data as DataSource)[this.fields.id] as any,
2283
+ null) : null;
2284
+ const dsIndex: number = (dataSource as DataSource[]).indexOf(data as DataSource);
2285
+ (dataSource as DataSource[]).splice(dsIndex, 1);
2286
+ this.setViewDataSource(listDataSource);
2287
+ if (foundData
2288
+ && foundData.parent
2289
+ && Array.isArray(foundData.parent[this.fields.child])
2290
+ && foundData.parent[this.fields.child].length <= 0) {
2291
+ const parentLi: HTMLElement | null = this.getLiFromObjOrElement(foundData.parent);
2292
+ if (parentLi) {
2293
+ const li: HTMLElement[] = ListBase.createListItemFromJson(
2294
+ this.createElement,
2295
+ [foundData.parent],
2296
+ this.listBaseOption,
2297
+ null,
2298
+ null,
2299
+ this);
2300
+ this.setAttributes(li);
2301
+ parentLi.parentElement.insertBefore(li[0], parentLi);
2302
+ parentLi.parentElement.removeChild(parentLi);
2303
+ }
2304
+ }
2305
+ if (dataSource.length <= 0) {
2306
+ this.back();
2307
+ }
2308
+ this.liCollection = Array.prototype.slice.call(this.element.querySelectorAll('.' + classNames.listItem));
2309
+ }
2310
+ }
2311
+
2312
+ // validate before removing an element whether the current view is inside target element's child view
2313
+ private validateNestedView(li: HTMLElement | null): void {
2314
+ const liID: string = li ? li.getAttribute('data-uid').toString().toLowerCase() : null;
2315
+ if (liID && this.curDSLevel && this.curDSLevel.length > 0) {
2316
+ while (this.curDSLevel.some((id: string) => id.toString().toLowerCase() === liID)) {
2317
+ this.back();
2318
+ }
2319
+ }
2320
+ }
2321
+
2322
+ /**
2323
+ * Removes multiple items from the ListView by passing the array of elements or array of field objects.
2324
+ *
2325
+ * @param {Fields[] | HTMLElement[] | Element[]} item - We can pass array of elements or array of field Object with ID and Text fields.
2326
+ */
2327
+
2328
+ public removeMultipleItems(item: HTMLElement[] | Element[] | Fields[]): void {
2329
+ if (item.length) {
2330
+ for (let i: number = 0; i < item.length; i++) {
2331
+ this.removeItem(item[i]);
2332
+ }
2333
+ }
2334
+ }
2335
+
2336
+ // eslint-disable-next-line
2337
+ private findParent(dataSource: any, id: string, callback: Function, parent: object): object {
2338
+ // eslint-disable-next-line no-prototype-builtins
2339
+ if (dataSource.hasOwnProperty(id) && callback(dataSource[id]) === true) {
2340
+ return extend({}, dataSource);
2341
+ }
2342
+
2343
+ for (let i: number = 0; i < Object.keys(dataSource).length; i++) {
2344
+ if (dataSource[Object.keys(dataSource)[i]]
2345
+ && typeof dataSource[Object.keys(dataSource)[i]] === 'object') {
2346
+ // eslint-disable-next-line
2347
+ const result: any = this.findParent(dataSource[Object.keys(dataSource)[i]], id, callback, dataSource);
2348
+ if (result != null) {
2349
+ if (!result.parent) { result.parent = parent; }
2350
+ return result;
2351
+ }
2352
+ }
2353
+ }
2354
+ return null;
2355
+ }
2356
+
2357
+ // Module Required function
2358
+ protected getModuleName(): string {
2359
+ return 'listview';
2360
+ }
2361
+
2362
+ public requiredModules(): ModuleDeclaration[] {
2363
+ const modules: ModuleDeclaration[] = [];
2364
+ if (this.enableVirtualization) {
2365
+ modules.push({ args: [this], member: 'virtualization' });
2366
+ }
2367
+
2368
+ return modules;
2369
+ }
2370
+
2371
+
2372
+ /**
2373
+ * Get the properties to be maintained in the persisted state.
2374
+ */
2375
+
2376
+ protected getPersistData(): string {
2377
+ return this.addOnPersist(['cssClass', 'enableRtl', 'htmlAttributes',
2378
+ 'enable', 'fields', 'animation', 'headerTitle',
2379
+ 'sortOrder', 'showIcon', 'height', 'width', 'showCheckBox', 'checkBoxPosition']);
2380
+ }
2381
+
2382
+ }
2383
+
2384
+ interface ResultData {
2385
+ result: { [key: string]: object }[];
2386
+ }
2387
+
2388
+ /** @hidden */
2389
+ export interface ClassNames {
2390
+ root: string;
2391
+ hover: string;
2392
+ focused: string;
2393
+ selected: string;
2394
+ parentItem: string;
2395
+ listItem: string;
2396
+ hasChild: string;
2397
+ view: string;
2398
+ header: string;
2399
+ text: string;
2400
+ headerText: string;
2401
+ headerTemplateText: string;
2402
+ listItemText: string;
2403
+ listIcon: string;
2404
+ textContent: string;
2405
+ groupListItem: string;
2406
+ disable: string;
2407
+ content: string;
2408
+ backIcon: string;
2409
+ backButton: string;
2410
+ icon: string;
2411
+ checkboxWrapper: string;
2412
+ checkbox: string;
2413
+ checked: string;
2414
+ checkboxIcon: string;
2415
+ checklist: string;
2416
+ checkboxRight: string;
2417
+ checkboxLeft: string;
2418
+ listviewCheckbox: string;
2419
+ itemCheckList: string;
2420
+ virtualElementContainer: string;
2421
+ }
2422
+
2423
+ /**
2424
+ * An interface that holds list selected item.
2425
+ */
2426
+ export interface ListSelectedItem {
2427
+ /**
2428
+ * Specifies the selected item dataSource collection.
2429
+ *
2430
+ * @isGenericType true
2431
+ */
2432
+ data?: object[];
2433
+ /**
2434
+ * Specifies the selected item text collection.
2435
+ */
2436
+ text?: string[];
2437
+ /**
2438
+ * Specifies index of the selected element.
2439
+ * Available only in virtualization.
2440
+ */
2441
+ index?: number[];
2442
+ /**
2443
+ * Specifies the hierarchical parent id collection of the current view.
2444
+ * Available only in nested list with checkbox enabled.
2445
+ */
2446
+ parentId?: string[];
2447
+ }
2448
+
2449
+ /**
2450
+ * An interface that holds selected item.
2451
+ */
2452
+ export interface SelectedItem {
2453
+ /**
2454
+ * It denotes the selected item text.
2455
+ */
2456
+ text: string;
2457
+
2458
+ /**
2459
+ * It denotes the selected item list element.
2460
+ *
2461
+ */
2462
+ item: HTMLElement | Element;
2463
+
2464
+ /**
2465
+ * It denotes the selected item dataSource JSON object.
2466
+ *
2467
+ * @isGenericType true
2468
+ */
2469
+ data: { [key: string]: Object } | string[] | number[];
2470
+
2471
+ }
2472
+
2473
+ /**
2474
+ * An interface that holds selected collection.
2475
+ */
2476
+ export interface SelectedCollection {
2477
+ /**
2478
+ * It denotes the selected item text data or collection.
2479
+ */
2480
+ text: string | string[];
2481
+
2482
+ /**
2483
+ * It denotes the selected item list element or element collection.
2484
+ */
2485
+ item: HTMLElement | Element[] | NodeList;
2486
+
2487
+ /**
2488
+ * It denotes the selected item dataSource JSON object or object collection.
2489
+ *
2490
+ * @isGenericType true
2491
+ */
2492
+ data: { [key: string]: object } | { [key: string]: object }[] | string[] | number[];
2493
+ }
2494
+
2495
+ /**
2496
+ * An interface that holds UI selected item.
2497
+ */
2498
+ export interface UISelectedItem {
2499
+ /**
2500
+ * It denotes the selected item text data or collection.
2501
+ */
2502
+ text: string | number | string[] | number[];
2503
+
2504
+ /**
2505
+ * It denotes the selected item list element or element collection.
2506
+ */
2507
+ item?: HTMLElement | Element[] | NodeList;
2508
+
2509
+ /**
2510
+ * It denotes the selected item dataSource JSON object or object collection.
2511
+ *
2512
+ * @isGenericType true
2513
+ */
2514
+ data: { [key: string]: object } | { [key: string]: object }[] | string | number | string[] | number[];
2515
+ /**
2516
+ * It is used to denote the index of the selected element.
2517
+ */
2518
+ index?: number | number[];
2519
+ /**
2520
+ * It is used to check whether the element is checked or not.
2521
+ */
2522
+ isChecked?: boolean;
2523
+ }
2524
+
2525
+ /**
2526
+ * An interface that holds details of data and parent.
2527
+ */
2528
+ export interface DataAndParent {
2529
+ /**
2530
+ * It denotes the selected item dataSource JSON object or object collection.
2531
+ *
2532
+ * @isGenericType true
2533
+ */
2534
+ data: { [key: string]: object } | { [key: string]: object }[] | string[];
2535
+ /**
2536
+ * It denotes the selected item's parent id;
2537
+ */
2538
+ parentId: string[];
2539
+ }
2540
+
2541
+ /**
2542
+ * An interface that holds nested list data.
2543
+ */
2544
+ export interface NestedListData {
2545
+ /**
2546
+ * It denotes the selected item text data or collection.
2547
+ */
2548
+ text: string | string[];
2549
+ /**
2550
+ * It denotes the selected item list element or element collection.
2551
+ */
2552
+ item: HTMLElement | Element[] | NodeList;
2553
+ /**
2554
+ * It denotes the selected item dataSource JSON object with it's parent id.
2555
+ */
2556
+ data: DataAndParent[];
2557
+ }
2558
+
2559
+ /**
2560
+ * An interface that holds selected event arguments
2561
+ */
2562
+ export interface SelectEventArgs extends BaseEventArgs, SelectedItem {
2563
+ /**
2564
+ * Specifies that event has triggered by user interaction.
2565
+ */
2566
+ isInteracted: boolean;
2567
+ /**
2568
+ * Specifies that event argument when event raised by other event.
2569
+ */
2570
+ event: MouseEvent | KeyboardEvent;
2571
+ /**
2572
+ * It is used to denote the index of the selected element.
2573
+ */
2574
+ index: number;
2575
+ /**
2576
+ * It is used to check whether the element is checked or not.
2577
+ */
2578
+ isChecked?: boolean;
2579
+ }
2580
+
2581
+ /**
2582
+ * An interface that holds item created event arguments
2583
+ */
2584
+ export interface ItemCreatedArgs {
2585
+ curData: { [key: string]: object };
2586
+ dataSource: { [key: string]: object } | string[];
2587
+ fields: FieldsMapping;
2588
+ item: HTMLElement;
2589
+ options: ListBaseOptions;
2590
+ text: string;
2591
+ }
2592
+
2593
+ interface DataSource {
2594
+ [key: string]: object;
2595
+ }