@syncfusion/ej2-lists 30.1.42 → 31.1.17

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