@syncfusion/ej2-lists 19.3.44 → 19.4.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/PULL_REQUEST_TEMPLATE/Bug.md +68 -0
- package/.github/PULL_REQUEST_TEMPLATE/Changelog.md +25 -0
- package/.github/PULL_REQUEST_TEMPLATE/Feature.md +31 -0
- package/dist/ej2-lists.umd.min.js +2 -2
- package/dist/ej2-lists.umd.min.js.map +1 -1
- package/dist/es6/ej2-lists.es2015.js +47 -21
- package/dist/es6/ej2-lists.es2015.js.map +1 -1
- package/dist/es6/ej2-lists.es5.js +47 -21
- package/dist/es6/ej2-lists.es5.js.map +1 -1
- package/dist/global/ej2-lists.min.js +2 -2
- package/dist/global/ej2-lists.min.js.map +1 -1
- package/dist/global/index.d.ts +1 -1
- package/dist/ts/common/list-base.ts +1283 -0
- package/dist/ts/component.ts +4 -0
- package/dist/ts/list-view/list-view.ts +2603 -0
- package/dist/ts/list-view/virtualization.ts +1144 -0
- package/dist/ts/sortable/sortable.ts +457 -0
- package/package.json +8 -8
- package/src/common/list-base.js +47 -21
- package/styles/list-view/_fluent-definition.scss +134 -0
- package/styles/list-view/_layout.scss +69 -5
- package/styles/list-view/_theme.scss +15 -5
- package/styles/list-view/icons/_fluent.scss +8 -0
|
@@ -0,0 +1,1144 @@
|
|
|
1
|
+
import { ListView, ItemCreatedArgs, classNames, Fields, UISelectedItem } from './list-view';
|
|
2
|
+
import { EventHandler, append, isNullOrUndefined, detach, removeClass, addClass, compile, formatUnit } from '@syncfusion/ej2-base';
|
|
3
|
+
import { debounce } from '@syncfusion/ej2-base';
|
|
4
|
+
import {ListBase} from '../common/list-base';
|
|
5
|
+
import { DataManager } from '@syncfusion/ej2-data';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ElementContext
|
|
9
|
+
*/
|
|
10
|
+
export interface ElementContext extends HTMLElement {
|
|
11
|
+
context: { [key: string]: string | object };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const listElementCount: number = 1.5;
|
|
15
|
+
const windowElementCount: number = 3;
|
|
16
|
+
|
|
17
|
+
export class Virtualization {
|
|
18
|
+
constructor(instance: ListView) {
|
|
19
|
+
this.listViewInstance = instance;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line
|
|
23
|
+
private listViewInstance: any;
|
|
24
|
+
private headerData: DataSource;
|
|
25
|
+
private templateData: DataSource;
|
|
26
|
+
private topElementHeight: number;
|
|
27
|
+
private bottomElementHeight: number;
|
|
28
|
+
public listItemHeight: number;
|
|
29
|
+
private domItemCount: number;
|
|
30
|
+
private expectedDomItemCount: number;
|
|
31
|
+
private scrollPosition: number;
|
|
32
|
+
private onVirtualScroll: EventListener;
|
|
33
|
+
private updateUl: EventListener;
|
|
34
|
+
private checkListWrapper: Node;
|
|
35
|
+
private iconCssWrapper: Node;
|
|
36
|
+
public uiFirstIndex: number;
|
|
37
|
+
private uiLastIndex: number;
|
|
38
|
+
private totalHeight: number;
|
|
39
|
+
private topElement: HTMLElement;
|
|
40
|
+
private bottomElement: HTMLElement;
|
|
41
|
+
private activeIndex: number;
|
|
42
|
+
private uiIndices: { [key: string]: number[] };
|
|
43
|
+
private listDiff: number;
|
|
44
|
+
private elementDifference: number = 0;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* For internal use only.
|
|
48
|
+
*
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
public isNgTemplate(): boolean {
|
|
53
|
+
return !isNullOrUndefined(this.listViewInstance.templateRef) && typeof this.listViewInstance.templateRef !== 'string';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* For internal use only.
|
|
58
|
+
*
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
public uiVirtualization(): void {
|
|
63
|
+
this.wireScrollEvent(false);
|
|
64
|
+
const curViewDS: { [key: string]: object; }[] = this.listViewInstance.curViewDS as { [key: string]: object; }[];
|
|
65
|
+
const firstDs: { [key: string]: object }[] = curViewDS.slice(0, 1);
|
|
66
|
+
this.listViewInstance.ulElement = this.listViewInstance.curUL = ListBase.createList(
|
|
67
|
+
// eslint-disable-next-line
|
|
68
|
+
this.listViewInstance.createElement, firstDs as any, this.listViewInstance.listBaseOption, null, this.listViewInstance);
|
|
69
|
+
this.listViewInstance.contentContainer = this.listViewInstance.createElement('div', { className: classNames.content });
|
|
70
|
+
this.listViewInstance.element.appendChild(this.listViewInstance.contentContainer);
|
|
71
|
+
this.listViewInstance.contentContainer.appendChild(this.listViewInstance.ulElement);
|
|
72
|
+
this.listItemHeight = this.listViewInstance.ulElement.firstElementChild.getBoundingClientRect().height;
|
|
73
|
+
this.expectedDomItemCount = this.ValidateItemCount(10000);
|
|
74
|
+
this.domItemCount = this.ValidateItemCount(Object.keys(this.listViewInstance.curViewDS).length);
|
|
75
|
+
this.uiFirstIndex = 0;
|
|
76
|
+
this.uiLastIndex = this.domItemCount - 1;
|
|
77
|
+
const otherDs: { [key: string]: object; }[] = curViewDS.slice(1, this.domItemCount);
|
|
78
|
+
const listItems: HTMLElement[] = ListBase.createListItemFromJson(
|
|
79
|
+
// eslint-disable-next-line
|
|
80
|
+
this.listViewInstance.createElement, otherDs as any, this.listViewInstance.listBaseOption, null, null, this.listViewInstance);
|
|
81
|
+
append(listItems, this.listViewInstance.ulElement);
|
|
82
|
+
this.listViewInstance.liCollection = <HTMLElement[] & NodeListOf<HTMLLIElement>>
|
|
83
|
+
this.listViewInstance.curUL.querySelectorAll('li');
|
|
84
|
+
this.topElement = this.listViewInstance.createElement('div');
|
|
85
|
+
this.listViewInstance.ulElement.insertBefore(this.topElement, this.listViewInstance.ulElement.firstElementChild);
|
|
86
|
+
this.bottomElement = this.listViewInstance.createElement('div');
|
|
87
|
+
this.listViewInstance.ulElement.insertBefore(this.bottomElement, null);
|
|
88
|
+
this.totalHeight = (Object.keys(curViewDS).length * this.listItemHeight) - (this.domItemCount * this.listItemHeight);
|
|
89
|
+
this.topElement.style.height = 0 + 'px';
|
|
90
|
+
this.bottomElement.style.height = this.totalHeight + 'px';
|
|
91
|
+
this.topElementHeight = 0;
|
|
92
|
+
this.bottomElementHeight = this.totalHeight;
|
|
93
|
+
this.listDiff = 0;
|
|
94
|
+
this.uiIndicesInitialization();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private wireScrollEvent(destroy: boolean): void {
|
|
98
|
+
if (!destroy) {
|
|
99
|
+
if (this.listViewInstance.isWindow) {
|
|
100
|
+
this.onVirtualScroll = this.onVirtualUiScroll.bind(this);
|
|
101
|
+
window.addEventListener('scroll', this.onVirtualScroll);
|
|
102
|
+
} else {
|
|
103
|
+
EventHandler.add(this.listViewInstance.element, 'scroll', this.onVirtualUiScroll, this);
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
if (this.listViewInstance.isWindow === true) {
|
|
107
|
+
window.removeEventListener('scroll', this.onVirtualScroll);
|
|
108
|
+
window.removeEventListener('scroll', this.updateUl);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
EventHandler.remove(this.listViewInstance.element, 'scroll', this.onVirtualUiScroll);
|
|
112
|
+
EventHandler.remove(this.listViewInstance.element, 'scroll', this.updateUlContainer);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private updateUlContainer(e: Event): void {
|
|
118
|
+
let listDiff: number;
|
|
119
|
+
const virtualElementContainer: HTMLElement = this.listViewInstance.ulElement.querySelector('.' + classNames.virtualElementContainer);
|
|
120
|
+
if (isNullOrUndefined(this.listViewInstance.liElementHeight)) {
|
|
121
|
+
this.listViewInstance.updateLiElementHeight();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (this.listViewInstance.isWindow) {
|
|
125
|
+
// eslint-disable-next-line
|
|
126
|
+
listDiff = Math.round((e as any).target.documentElement.scrollTop / this.listViewInstance.liElementHeight) - 2;
|
|
127
|
+
} else {
|
|
128
|
+
// eslint-disable-next-line
|
|
129
|
+
listDiff = Math.round((e as any).target.scrollTop / this.listViewInstance.liElementHeight) - 2;
|
|
130
|
+
}
|
|
131
|
+
if (((listDiff - 1) * this.listViewInstance.liElementHeight) < 0) {
|
|
132
|
+
virtualElementContainer.style.top = '0px';
|
|
133
|
+
} else {
|
|
134
|
+
virtualElementContainer.style.top = (listDiff) * this.listViewInstance.liElementHeight + 'px';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private ValidateItemCount(dataSourceLength: number): number {
|
|
139
|
+
const height: number = parseFloat(formatUnit(this.listViewInstance.height));
|
|
140
|
+
let itemCount: number;
|
|
141
|
+
if (this.listViewInstance.isWindow) {
|
|
142
|
+
itemCount = Math.round((window.innerHeight / this.listItemHeight) * windowElementCount);
|
|
143
|
+
} else {
|
|
144
|
+
if (typeof this.listViewInstance.height === 'string' && this.listViewInstance.height.indexOf('%') !== -1) {
|
|
145
|
+
// eslint-disable-next-line max-len
|
|
146
|
+
itemCount = Math.round((this.listViewInstance.element.getBoundingClientRect().height / this.listItemHeight) * listElementCount);
|
|
147
|
+
} else {
|
|
148
|
+
itemCount = Math.round((height / this.listItemHeight) * listElementCount);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (itemCount > dataSourceLength) {
|
|
152
|
+
itemCount = dataSourceLength;
|
|
153
|
+
}
|
|
154
|
+
return itemCount;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private uiIndicesInitialization(): void {
|
|
158
|
+
this.uiIndices = { 'activeIndices': [], 'disabledItemIndices': [], 'hiddenItemIndices': [] };
|
|
159
|
+
const data: DataSource[] = this.listViewInstance.curViewDS as DataSource[];
|
|
160
|
+
for (let i: number = 0; i < data.length; i++) {
|
|
161
|
+
if (this.listViewInstance.showCheckBox && data[i][this.listViewInstance.fields.isChecked]) {
|
|
162
|
+
this.uiIndices.activeIndices.push(i);
|
|
163
|
+
}
|
|
164
|
+
// eslint-disable-next-line
|
|
165
|
+
if (!isNullOrUndefined((data[i] as any)[this.listViewInstance.fields.enabled]) &&
|
|
166
|
+
!data[i][this.listViewInstance.fields.enabled]) {
|
|
167
|
+
// eslint-disable-next-line
|
|
168
|
+
(this.uiIndices.disabledItemIndices.push(i)) as any;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (this.isNgTemplate()) {
|
|
172
|
+
const items: ElementContext[] = this.listViewInstance.element.querySelectorAll('.' + classNames.listItem);
|
|
173
|
+
for (let index: number = 0; index < items.length; index++) {
|
|
174
|
+
items[index].context = this.listViewInstance.viewContainerRef.get(index).context;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public refreshItemHeight(): void {
|
|
180
|
+
if (this.listViewInstance.curViewDS.length) {
|
|
181
|
+
const curViewDS: { [key: string]: object; }[] = this.listViewInstance.curViewDS as { [key: string]: object; }[];
|
|
182
|
+
this.listItemHeight = (this.topElement.nextSibling as HTMLElement).getBoundingClientRect().height;
|
|
183
|
+
this.totalHeight = (Object.keys(curViewDS).length * this.listItemHeight) - (this.domItemCount * this.listItemHeight);
|
|
184
|
+
this.bottomElementHeight = this.totalHeight;
|
|
185
|
+
this.bottomElement.style.height = this.totalHeight + 'px';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private getscrollerHeight(startingHeight: number): number {
|
|
190
|
+
return this.listViewInstance.isWindow ? (((pageYOffset - startingHeight) <= 0) ? 0 :
|
|
191
|
+
(pageYOffset - startingHeight)) : ((this.listViewInstance.element.scrollTop - startingHeight) <= 0) ? 0 :
|
|
192
|
+
(this.listViewInstance.element.scrollTop - startingHeight);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private onVirtualUiScroll(e: Event): void {
|
|
196
|
+
let startingHeight: number;
|
|
197
|
+
if (this.listViewInstance.isWindow) {
|
|
198
|
+
startingHeight = this.listViewInstance.ulElement.getBoundingClientRect().top -
|
|
199
|
+
document.documentElement.getBoundingClientRect().top;
|
|
200
|
+
} else {
|
|
201
|
+
startingHeight = this.listViewInstance.headerEle ? this.listViewInstance.headerEle.getBoundingClientRect().height : 0;
|
|
202
|
+
}
|
|
203
|
+
this.scrollPosition = isNullOrUndefined(this.scrollPosition) ? 0 : this.scrollPosition;
|
|
204
|
+
const scroll: number = this.getscrollerHeight(startingHeight);
|
|
205
|
+
this.topElementHeight = this.listItemHeight * Math.floor(scroll / this.listItemHeight);
|
|
206
|
+
this.bottomElementHeight = this.totalHeight - this.topElementHeight;
|
|
207
|
+
[this.topElementHeight, this.bottomElementHeight] = scroll <= this.totalHeight ?
|
|
208
|
+
[this.topElementHeight, this.bottomElementHeight] : [this.totalHeight, 0];
|
|
209
|
+
if (this.topElementHeight !== parseFloat(this.topElement.style.height)) {
|
|
210
|
+
this.topElement.style.height = this.topElementHeight + 'px';
|
|
211
|
+
this.bottomElement.style.height = this.bottomElementHeight + 'px';
|
|
212
|
+
if (scroll > this.scrollPosition) {
|
|
213
|
+
const listDiff: number = Math.round(((this.topElementHeight / this.listItemHeight) - this.listDiff));
|
|
214
|
+
if (listDiff > (this.expectedDomItemCount + 5)) {
|
|
215
|
+
this.onLongScroll(listDiff, true);
|
|
216
|
+
} else {
|
|
217
|
+
this.onNormalScroll(listDiff, true);
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
const listDiff: number = Math.round((this.listDiff - (this.topElementHeight / this.listItemHeight)));
|
|
221
|
+
if (listDiff > (this.expectedDomItemCount + 5)) {
|
|
222
|
+
this.onLongScroll(listDiff, false);
|
|
223
|
+
} else {
|
|
224
|
+
this.onNormalScroll(listDiff, false);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
this.listDiff = Math.round(this.topElementHeight / this.listItemHeight);
|
|
229
|
+
if (typeof this.listViewInstance.onUIScrolled === 'function') {
|
|
230
|
+
this.listViewInstance.onUIScrolled();
|
|
231
|
+
}
|
|
232
|
+
this.scrollPosition = scroll;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private onLongScroll(listDiff: number, isScrollingDown: boolean): void {
|
|
236
|
+
let index: number = isScrollingDown ? (this.uiFirstIndex + listDiff) : (this.uiFirstIndex - listDiff);
|
|
237
|
+
const elements: HTMLElement[] = this.listViewInstance.ulElement.querySelectorAll('li');
|
|
238
|
+
for (let i: number = 0; i < elements.length; i++) {
|
|
239
|
+
this.updateUI(elements[i], index);
|
|
240
|
+
index++;
|
|
241
|
+
}
|
|
242
|
+
this.uiLastIndex = isScrollingDown ? (this.uiLastIndex + listDiff) : (this.uiLastIndex - listDiff);
|
|
243
|
+
this.uiFirstIndex = isScrollingDown ? (this.uiFirstIndex + listDiff) : (this.uiFirstIndex - listDiff);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private onNormalScroll(listDiff: number, isScrollingDown: boolean): void {
|
|
247
|
+
if (isScrollingDown) {
|
|
248
|
+
for (let i: number = 0; i < listDiff; i++) {
|
|
249
|
+
const index: number = ++this.uiLastIndex;
|
|
250
|
+
this.updateUI(this.topElement.nextElementSibling as HTMLElement, index, this.bottomElement);
|
|
251
|
+
this.uiFirstIndex++;
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
for (let i: number = 0; i < listDiff; i++) {
|
|
255
|
+
const index: number = --this.uiFirstIndex;
|
|
256
|
+
const target: HTMLElement = this.topElement.nextSibling as HTMLElement;
|
|
257
|
+
this.updateUI(this.bottomElement.previousElementSibling as HTMLElement, index, target);
|
|
258
|
+
this.uiLastIndex--;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private updateUiContent(element: HTMLElement, index: number): void {
|
|
264
|
+
const curViewDs: { [key: string]: any; }[] = this.listViewInstance.curViewDS as DataSource[];
|
|
265
|
+
if (typeof (this.listViewInstance.dataSource as string[])[0] === 'string' ||
|
|
266
|
+
typeof (this.listViewInstance.dataSource as number[])[0] === 'number') {
|
|
267
|
+
element.dataset.uid = ListBase.generateId();
|
|
268
|
+
element.getElementsByClassName(classNames.listItemText)[0].innerHTML =
|
|
269
|
+
(this.listViewInstance.curViewDS as string[] | number[])[index].toString();
|
|
270
|
+
} else {
|
|
271
|
+
// eslint-disable-next-line
|
|
272
|
+
element.dataset.uid = (curViewDs[index][this.listViewInstance.fields.id])as any ?
|
|
273
|
+
// eslint-disable-next-line
|
|
274
|
+
(curViewDs[index][this.listViewInstance.fields.id]) as any : (ListBase.generateId()as any);
|
|
275
|
+
element.getElementsByClassName(classNames.listItemText)[0].innerHTML =
|
|
276
|
+
// eslint-disable-next-line
|
|
277
|
+
(curViewDs[index][this.listViewInstance.fields.text]) as any;
|
|
278
|
+
}
|
|
279
|
+
if (this.listViewInstance.showIcon) {
|
|
280
|
+
if (element.querySelector('.' + classNames.listIcon)) {
|
|
281
|
+
detach(element.querySelector('.' + classNames.listIcon));
|
|
282
|
+
}
|
|
283
|
+
if ((this.listViewInstance.curViewDS[index] as { [key: string]: object; })[this.listViewInstance.fields.iconCss]) {
|
|
284
|
+
const textContent: Element = element.querySelector('.' + classNames.textContent);
|
|
285
|
+
const target: Element = this.listViewInstance.createElement('div', {
|
|
286
|
+
className: classNames.listIcon + ' ' +
|
|
287
|
+
(this.listViewInstance.curViewDS[index] as { [key: string]: object; })[this.listViewInstance.fields.iconCss]
|
|
288
|
+
});
|
|
289
|
+
textContent.insertBefore(target, element.querySelector('.' + classNames.listItemText));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (this.listViewInstance.showCheckBox && this.listViewInstance.fields.groupBy) {
|
|
293
|
+
if (!this.checkListWrapper) {
|
|
294
|
+
this.checkListWrapper = this.listViewInstance.curUL.querySelector('.' + classNames.checkboxWrapper).cloneNode(true);
|
|
295
|
+
}
|
|
296
|
+
const textContent: Element = element.querySelector('.' + classNames.textContent);
|
|
297
|
+
if ((this.listViewInstance.curViewDS[index] as { [key: string]: object; }).isHeader) {
|
|
298
|
+
if (element.querySelector('.' + classNames.checkboxWrapper)) {
|
|
299
|
+
element.classList.remove(classNames.checklist);
|
|
300
|
+
textContent.classList.remove(classNames.checkbox);
|
|
301
|
+
detach(element.querySelector('.' + classNames.checkboxWrapper));
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
if (!element.querySelector('.' + classNames.checkboxWrapper)) {
|
|
305
|
+
element.classList.add(classNames.checklist);
|
|
306
|
+
textContent.classList.add(classNames.checkbox);
|
|
307
|
+
textContent.insertBefore(this.checkListWrapper.cloneNode(true), element.querySelector('.' + classNames.listItemText));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
private changeElementAttributes(element: Element, index: number): void {
|
|
313
|
+
element.classList.remove(classNames.disable);
|
|
314
|
+
if (this.uiIndices.disabledItemIndices.length && this.uiIndices.disabledItemIndices.indexOf(index) !== -1) {
|
|
315
|
+
element.classList.add(classNames.disable);
|
|
316
|
+
}
|
|
317
|
+
(element as HTMLElement).style.display = '';
|
|
318
|
+
if (this.uiIndices.hiddenItemIndices.length && this.uiIndices.hiddenItemIndices.indexOf(index) !== -1) {
|
|
319
|
+
(element as HTMLElement).style.display = 'none';
|
|
320
|
+
}
|
|
321
|
+
if (this.listViewInstance.showCheckBox) {
|
|
322
|
+
const checklistElement: Element = element.querySelector('.' + classNames.checkboxWrapper);
|
|
323
|
+
element.classList.remove(classNames.selected);
|
|
324
|
+
element.classList.remove(classNames.focused);
|
|
325
|
+
if (checklistElement) {
|
|
326
|
+
checklistElement.removeAttribute('aria-checked');
|
|
327
|
+
checklistElement.firstElementChild.classList.remove(classNames.checked);
|
|
328
|
+
}
|
|
329
|
+
if (this.uiIndices.activeIndices.length && this.uiIndices.activeIndices.indexOf(index) !== -1 &&
|
|
330
|
+
!this.listViewInstance.curUL.querySelector(classNames.selected)) {
|
|
331
|
+
element.classList.add(classNames.selected);
|
|
332
|
+
checklistElement.firstElementChild.classList.add(classNames.checked);
|
|
333
|
+
checklistElement.setAttribute('aria-checked', 'true');
|
|
334
|
+
if (this.activeIndex === index) {
|
|
335
|
+
element.classList.add(classNames.focused);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
} else {
|
|
339
|
+
element.classList.remove(classNames.selected);
|
|
340
|
+
element.removeAttribute('aria-selected');
|
|
341
|
+
if (!isNullOrUndefined(this.activeIndex) && this.activeIndex === index &&
|
|
342
|
+
!this.listViewInstance.curUL.querySelector(classNames.selected)) {
|
|
343
|
+
element.classList.add(classNames.selected);
|
|
344
|
+
element.setAttribute('aria-selected', 'true');
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (this.listViewInstance.fields.groupBy) {
|
|
348
|
+
if ((this.listViewInstance.curViewDS as { [key: string]: object; }[])[index].isHeader) {
|
|
349
|
+
if (element.classList.contains(classNames.listItem)) {
|
|
350
|
+
element.classList.remove(classNames.listItem);
|
|
351
|
+
element.setAttribute('role', 'group');
|
|
352
|
+
element.classList.add(classNames.groupListItem);
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
if (element.classList.contains(classNames.groupListItem)) {
|
|
356
|
+
element.classList.remove(classNames.groupListItem);
|
|
357
|
+
element.setAttribute('role', 'listitem');
|
|
358
|
+
element.classList.add(classNames.listItem);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private findDSAndIndexFromId(
|
|
365
|
+
ds: DataSource[] | string[] | number[], fields: Fields | Element): DataSource {
|
|
366
|
+
const resultJSON: { [key: string]: object | number } = {};
|
|
367
|
+
fields = this.listViewInstance.getElementUID(fields);
|
|
368
|
+
if (!isNullOrUndefined(fields)) {
|
|
369
|
+
(ds as { [key: string]: object; }[]).some((data: { [key: string]: object; }, index: number) => {
|
|
370
|
+
if (((fields as { [key: string]: object; })[this.listViewInstance.fields.id] &&
|
|
371
|
+
// eslint-disable-next-line
|
|
372
|
+
(fields as { [key: string]: any ; })[this.listViewInstance.fields.id]
|
|
373
|
+
// eslint-disable-next-line
|
|
374
|
+
=== (data[this.listViewInstance.fields.id] && data[this.listViewInstance.fields.id] as any) || fields === data)) {
|
|
375
|
+
resultJSON.index = index;
|
|
376
|
+
resultJSON.data = data;
|
|
377
|
+
return true;
|
|
378
|
+
} else {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return resultJSON as DataSource;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
public getSelectedItems(): UISelectedItem {
|
|
388
|
+
if (!isNullOrUndefined(this.activeIndex) || (this.listViewInstance.showCheckBox && this.uiIndices.activeIndices.length)) {
|
|
389
|
+
const dataCollection: string[] | { [key: string]: object; }[] = [];
|
|
390
|
+
const textCollection: string[] = [];
|
|
391
|
+
if (typeof (this.listViewInstance.dataSource as string[])[0] === 'string' ||
|
|
392
|
+
typeof (this.listViewInstance.dataSource as number[])[0] === 'number') {
|
|
393
|
+
const curViewDS: string[] | number[] = this.listViewInstance.curViewDS as string[] | number[];
|
|
394
|
+
if (this.listViewInstance.showCheckBox) {
|
|
395
|
+
const indices: number[] = this.uiIndices.activeIndices;
|
|
396
|
+
for (let i: number = 0; i < indices.length; i++) {
|
|
397
|
+
(dataCollection as string[]).push((curViewDS as string[])[indices[i]]);
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
text: dataCollection as string[],
|
|
401
|
+
// eslint-disable-next-line
|
|
402
|
+
data: dataCollection as any,
|
|
403
|
+
index: this.uiIndices.activeIndices.map((index: number) =>
|
|
404
|
+
(this.listViewInstance.dataSource as string[]).indexOf((curViewDS as string[])[index]))
|
|
405
|
+
};
|
|
406
|
+
} else {
|
|
407
|
+
return {
|
|
408
|
+
text: curViewDS[this.activeIndex],
|
|
409
|
+
data: curViewDS[this.activeIndex],
|
|
410
|
+
index: (this.listViewInstance.dataSource as string[]).indexOf((curViewDS as string[])[this.activeIndex])
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
} else {
|
|
414
|
+
const curViewDS: { [key: string]: object | string }[] = this.listViewInstance.curViewDS as { [key: string]: object; }[];
|
|
415
|
+
const text: string = this.listViewInstance.fields.text;
|
|
416
|
+
if (this.listViewInstance.showCheckBox) {
|
|
417
|
+
const indexArray: number[] = this.uiIndices.activeIndices;
|
|
418
|
+
for (let i: number = 0; i < indexArray.length; i++) {
|
|
419
|
+
textCollection.push((curViewDS[indexArray[i]] as { [key: string]: string; })[text]);
|
|
420
|
+
(dataCollection as { [key: string]: any; }[]).push(curViewDS[indexArray[i]] as DataSource);
|
|
421
|
+
}
|
|
422
|
+
const dataSource: { [key: string]: any; }[] =
|
|
423
|
+
this.listViewInstance.dataSource instanceof DataManager
|
|
424
|
+
? curViewDS : this.listViewInstance.dataSource;
|
|
425
|
+
return {
|
|
426
|
+
text: textCollection,
|
|
427
|
+
// eslint-disable-next-line
|
|
428
|
+
data: dataCollection as any,
|
|
429
|
+
index: this.uiIndices.activeIndices.map((index: number) =>
|
|
430
|
+
dataSource.indexOf(curViewDS[index] as DataSource))
|
|
431
|
+
};
|
|
432
|
+
} else {
|
|
433
|
+
const dataSource: { [key: string]: any; }[] =
|
|
434
|
+
this.listViewInstance.dataSource instanceof DataManager
|
|
435
|
+
? curViewDS : this.listViewInstance.dataSource;
|
|
436
|
+
return {
|
|
437
|
+
text: curViewDS[this.activeIndex][this.listViewInstance.fields.text] as string,
|
|
438
|
+
// eslint-disable-next-line
|
|
439
|
+
data: curViewDS[this.activeIndex] as any,
|
|
440
|
+
index: dataSource.indexOf(curViewDS[this.activeIndex])
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
} else {
|
|
445
|
+
return undefined;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
public selectItem(obj: Fields | HTMLElement | Element): void {
|
|
450
|
+
const resutJSON: { [key: string]: any | number } = this.findDSAndIndexFromId(this.listViewInstance.curViewDS, obj);
|
|
451
|
+
if (Object.keys(resutJSON).length) {
|
|
452
|
+
const isSelected: boolean = this.activeIndex === resutJSON.index;
|
|
453
|
+
let isChecked: boolean;
|
|
454
|
+
this.activeIndex = resutJSON.index as number;
|
|
455
|
+
if (this.listViewInstance.showCheckBox) {
|
|
456
|
+
if (this.uiIndices.activeIndices.indexOf(resutJSON.index as number) === -1) {
|
|
457
|
+
isChecked = true;
|
|
458
|
+
this.uiIndices.activeIndices.push(resutJSON.index as number);
|
|
459
|
+
} else {
|
|
460
|
+
isChecked = false;
|
|
461
|
+
this.uiIndices.activeIndices.splice(this.uiIndices.activeIndices.indexOf(resutJSON.index as number), 1);
|
|
462
|
+
}
|
|
463
|
+
if (this.listViewInstance.curUL.querySelector('.' + classNames.focused)) {
|
|
464
|
+
this.listViewInstance.curUL.querySelector('.' + classNames.focused).classList.remove(classNames.focused);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
if (this.listViewInstance.getLiFromObjOrElement(obj)) {
|
|
468
|
+
if (this.listViewInstance.showCheckBox) {
|
|
469
|
+
this.listViewInstance.setCheckboxLI(this.listViewInstance.getLiFromObjOrElement(obj));
|
|
470
|
+
} else {
|
|
471
|
+
this.listViewInstance.setSelectLI(this.listViewInstance.getLiFromObjOrElement(obj));
|
|
472
|
+
}
|
|
473
|
+
} else {
|
|
474
|
+
let eventArgs: { [key: string]: any; };
|
|
475
|
+
if (typeof (this.listViewInstance.dataSource as string[])[0] === 'string' ||
|
|
476
|
+
typeof (this.listViewInstance.dataSource as number[])[0] === 'number') {
|
|
477
|
+
eventArgs = {
|
|
478
|
+
text: (this.listViewInstance.curViewDS as number[] | string[])[this.activeIndex],
|
|
479
|
+
data: (this.listViewInstance.curViewDS as number[] | string[])[this.activeIndex],
|
|
480
|
+
index: this.activeIndex
|
|
481
|
+
};
|
|
482
|
+
} else {
|
|
483
|
+
const curViewDS: { [key: string]: object; }[] = this.listViewInstance.curViewDS as { [key: string]: object; }[];
|
|
484
|
+
eventArgs = {
|
|
485
|
+
text: curViewDS[this.activeIndex][this.listViewInstance.fields.text],
|
|
486
|
+
data: curViewDS[this.activeIndex],
|
|
487
|
+
index: this.activeIndex
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
if (this.listViewInstance.showCheckBox) {
|
|
491
|
+
eventArgs.isChecked = isChecked;
|
|
492
|
+
this.listViewInstance.trigger('select', eventArgs);
|
|
493
|
+
} else if (!isSelected) {
|
|
494
|
+
this.listViewInstance.removeSelect();
|
|
495
|
+
this.listViewInstance.trigger('select', eventArgs);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
} else if (isNullOrUndefined(obj) && !this.listViewInstance.showCheckBox) {
|
|
499
|
+
this.listViewInstance.removeSelect();
|
|
500
|
+
this.activeIndex = undefined;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
public enableItem(obj: Fields | HTMLElement | Element): void {
|
|
505
|
+
const resutJSON: { [key: string]: any | number } = this.findDSAndIndexFromId(this.listViewInstance.curViewDS, obj);
|
|
506
|
+
if (Object.keys(resutJSON).length) {
|
|
507
|
+
this.uiIndices.disabledItemIndices.splice(this.uiIndices.disabledItemIndices.indexOf(resutJSON.index as number), 1);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
public disableItem(obj: Fields | HTMLElement | Element): void {
|
|
512
|
+
const resutJSON: { [key: string]: any | number } = this.findDSAndIndexFromId(this.listViewInstance.curViewDS, obj);
|
|
513
|
+
if (Object.keys(resutJSON).length && this.uiIndices.disabledItemIndices.indexOf(resutJSON.index as number) === -1) {
|
|
514
|
+
this.uiIndices.disabledItemIndices.push(resutJSON.index as number);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
public showItem(obj: Fields | HTMLElement | Element): void {
|
|
518
|
+
const resutJSON: { [key: string]: any | number } = this.findDSAndIndexFromId(this.listViewInstance.curViewDS, obj);
|
|
519
|
+
if (Object.keys(resutJSON).length) {
|
|
520
|
+
this.uiIndices.hiddenItemIndices.splice(this.uiIndices.hiddenItemIndices.indexOf(resutJSON.index as number), 1);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
public hideItem(obj: Fields | HTMLElement | Element): void {
|
|
525
|
+
const resutJSON: { [key: string]: any | number } = this.findDSAndIndexFromId(this.listViewInstance.curViewDS, obj);
|
|
526
|
+
if (Object.keys(resutJSON).length && this.uiIndices.hiddenItemIndices.indexOf(resutJSON.index as number) === -1) {
|
|
527
|
+
this.uiIndices.hiddenItemIndices.push(resutJSON.index as number);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
public removeItem(obj: HTMLElement | Element | Fields): void {
|
|
532
|
+
let dataSource: DataSource;
|
|
533
|
+
const curViewDS: DataSource[] = this.listViewInstance.curViewDS;
|
|
534
|
+
const resutJSON: { [key: string]: any | number } = this.findDSAndIndexFromId(curViewDS, obj);
|
|
535
|
+
if (Object.keys(resutJSON).length) {
|
|
536
|
+
dataSource = resutJSON.data as DataSource;
|
|
537
|
+
if (curViewDS[(resutJSON.index as number) - 1] &&
|
|
538
|
+
curViewDS[(resutJSON.index as number) - 1].isHeader &&
|
|
539
|
+
((curViewDS[(resutJSON.index as number) - 1])
|
|
540
|
+
.items as { [key: string]: any; }[]).length === 1) {
|
|
541
|
+
this.removeUiItem((resutJSON.index as number) - 1);
|
|
542
|
+
this.removeUiItem((resutJSON.index as number) - 1);
|
|
543
|
+
} else {
|
|
544
|
+
this.removeUiItem((resutJSON.index as number));
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
const listDataSource: DataSource[] = this.listViewInstance.dataSource instanceof DataManager
|
|
548
|
+
? this.listViewInstance.localData : this.listViewInstance.dataSource;
|
|
549
|
+
const index: number = listDataSource.indexOf(dataSource);
|
|
550
|
+
if (index !== -1) {
|
|
551
|
+
listDataSource.splice(index, 1);
|
|
552
|
+
this.listViewInstance.setViewDataSource(listDataSource);
|
|
553
|
+
}
|
|
554
|
+
// recollect all the list item into collection
|
|
555
|
+
|
|
556
|
+
this.listViewInstance.liCollection =
|
|
557
|
+
<HTMLElement[] & NodeListOf<HTMLLIElement>>this.listViewInstance.curUL.querySelectorAll('li');
|
|
558
|
+
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// eslint-disable-next-line
|
|
562
|
+
public setCheckboxLI(li: HTMLElement | Element, e?: MouseEvent | KeyboardEvent | FocusEvent): void {
|
|
563
|
+
const index: number = Array.prototype.indexOf.call(this.listViewInstance.curUL.querySelectorAll('li'), li) + this.uiFirstIndex;
|
|
564
|
+
this.activeIndex = Array.prototype.indexOf.call(this.listViewInstance.curUL.querySelectorAll('li'), li) + this.uiFirstIndex;
|
|
565
|
+
if (li.classList.contains(classNames.selected)) {
|
|
566
|
+
if (this.uiIndices.activeIndices.indexOf(index) === -1) {
|
|
567
|
+
this.uiIndices.activeIndices.push(index);
|
|
568
|
+
}
|
|
569
|
+
} else {
|
|
570
|
+
this.uiIndices.activeIndices.splice(this.uiIndices.activeIndices.indexOf(index), 1);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// eslint-disable-next-line
|
|
575
|
+
public setSelectLI(li: HTMLElement | Element, e?: MouseEvent | KeyboardEvent | FocusEvent): void {
|
|
576
|
+
this.activeIndex = Array.prototype.indexOf.call(this.listViewInstance.curUL.querySelectorAll('li'), li) + this.uiFirstIndex;
|
|
577
|
+
}
|
|
578
|
+
public checkedItem(checked: boolean): void {
|
|
579
|
+
if (checked) {
|
|
580
|
+
this.uiIndices.activeIndices = [];
|
|
581
|
+
this.activeIndex = undefined;
|
|
582
|
+
const data: DataSource[] = this.listViewInstance.curViewDS as DataSource[];
|
|
583
|
+
for (let index: number = 0; index < data.length; index++) {
|
|
584
|
+
if (!data[index].isHeader) {
|
|
585
|
+
this.uiIndices.activeIndices.push(index);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
} else {
|
|
590
|
+
this.activeIndex = undefined;
|
|
591
|
+
this.uiIndices.activeIndices = [];
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
private addUiItem(index: number): void {
|
|
597
|
+
// virtually new add list item based on the scollbar position
|
|
598
|
+
// if the scroll bar is at the top, just pretend the new item has been added since no UI
|
|
599
|
+
// change is required for the item that has been added at last but when scroll bar is at the bottom
|
|
600
|
+
// just detach top and inject into bottom to mimic new item is added
|
|
601
|
+
const curViewDs: DataSource[] = this.listViewInstance.curViewDS as DataSource[];
|
|
602
|
+
this.changeUiIndices(index, true);
|
|
603
|
+
if (this.activeIndex && this.activeIndex >= index) {
|
|
604
|
+
this.activeIndex++;
|
|
605
|
+
}
|
|
606
|
+
if (this.listViewInstance.showCheckBox &&
|
|
607
|
+
(curViewDs[index] as { [key: string]: object; })[this.listViewInstance.fields.isChecked]) {
|
|
608
|
+
this.uiIndices.activeIndices.push(index);
|
|
609
|
+
}
|
|
610
|
+
if (!parseFloat(this.bottomElement.style.height) && !parseFloat(this.topElement.style.height)) {
|
|
611
|
+
this.bottomElement.style.height = parseFloat(this.bottomElement.style.height) + this.listItemHeight + 'px';
|
|
612
|
+
}
|
|
613
|
+
if (parseFloat(this.bottomElement.style.height)) {
|
|
614
|
+
const liItem: HTMLElement = this.listViewInstance.curUL.lastElementChild.previousSibling as HTMLElement;
|
|
615
|
+
const target: HTMLElement = this.listViewInstance.getLiFromObjOrElement(curViewDs[index + 1]) ||
|
|
616
|
+
this.listViewInstance.getLiFromObjOrElement(curViewDs[index + 2]);
|
|
617
|
+
if (target) {
|
|
618
|
+
this.bottomElement.style.height = parseFloat(this.bottomElement.style.height) + this.listItemHeight + 'px';
|
|
619
|
+
this.updateUI(liItem, index, target);
|
|
620
|
+
}
|
|
621
|
+
} else {
|
|
622
|
+
const liItem: HTMLElement = this.listViewInstance.curUL.firstElementChild.nextSibling as HTMLElement;
|
|
623
|
+
let target: HTMLElement;
|
|
624
|
+
if ((Object.keys(this.listViewInstance.curViewDS).length - 1) === index) {
|
|
625
|
+
target = this.listViewInstance.curUL.lastElementChild as HTMLElement;
|
|
626
|
+
} else {
|
|
627
|
+
target = this.listViewInstance.getLiFromObjOrElement(curViewDs[index + 1]) ||
|
|
628
|
+
this.listViewInstance.getLiFromObjOrElement(curViewDs[index + 2]);
|
|
629
|
+
}
|
|
630
|
+
this.topElement.style.height = parseFloat(this.topElement.style.height) + this.listItemHeight + 'px';
|
|
631
|
+
this.uiFirstIndex++;
|
|
632
|
+
this.uiLastIndex++;
|
|
633
|
+
if (target) {
|
|
634
|
+
this.updateUI(liItem, index, target);
|
|
635
|
+
if (this.listViewInstance.isWindow === true)
|
|
636
|
+
{
|
|
637
|
+
window.scrollTo(0, (pageYOffset + this.listItemHeight));
|
|
638
|
+
}
|
|
639
|
+
else{ this.listViewInstance.element.scrollTop += this.listItemHeight;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
this.totalHeight += this.listItemHeight;
|
|
644
|
+
this.listDiff = Math.round(parseFloat(this.topElement.style.height) / this.listItemHeight);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
private removeUiItem(index: number): void {
|
|
648
|
+
this.totalHeight -= this.listItemHeight;
|
|
649
|
+
const curViewDS: DataSource = (this.listViewInstance.curViewDS as { [key: string]: object; }[])[index];
|
|
650
|
+
const liItem: HTMLElement = this.listViewInstance.getLiFromObjOrElement(curViewDS);
|
|
651
|
+
(this.listViewInstance.curViewDS as { [key: string]: object; }[]).splice(index, 1);
|
|
652
|
+
if (this.activeIndex && this.activeIndex >= index) {
|
|
653
|
+
this.activeIndex--;
|
|
654
|
+
}
|
|
655
|
+
if (liItem) {
|
|
656
|
+
if (this.domItemCount > Object.keys(this.listViewInstance.curViewDS).length) {
|
|
657
|
+
detach(liItem);
|
|
658
|
+
this.domItemCount--;
|
|
659
|
+
this.uiLastIndex--;
|
|
660
|
+
this.totalHeight = 0;
|
|
661
|
+
} else {
|
|
662
|
+
if (liItem.classList.contains(classNames.disable)) {
|
|
663
|
+
liItem.classList.remove(classNames.disable);
|
|
664
|
+
this.uiIndices.disabledItemIndices.splice(this.uiIndices.disabledItemIndices.indexOf(index), 1);
|
|
665
|
+
}
|
|
666
|
+
if (liItem.style.display === 'none') {
|
|
667
|
+
liItem.style.display = '';
|
|
668
|
+
this.uiIndices.hiddenItemIndices.splice(this.uiIndices.hiddenItemIndices.indexOf(index), 1);
|
|
669
|
+
}
|
|
670
|
+
if (this.listViewInstance.showCheckBox && liItem.classList.contains(classNames.selected)) {
|
|
671
|
+
this.listViewInstance.removeSelect();
|
|
672
|
+
this.uiIndices.activeIndices.splice(this.uiIndices.activeIndices.indexOf(index), 1);
|
|
673
|
+
const checklistElement: Element = liItem.querySelector('.' + classNames.checkboxWrapper);
|
|
674
|
+
checklistElement.removeAttribute('aria-checked');
|
|
675
|
+
checklistElement.firstElementChild.classList.remove(classNames.checked);
|
|
676
|
+
if (liItem.classList.contains(classNames.focused)) {
|
|
677
|
+
liItem.classList.remove(classNames.focused);
|
|
678
|
+
this.activeIndex = undefined;
|
|
679
|
+
}
|
|
680
|
+
} else if (liItem.classList.contains(classNames.selected)) {
|
|
681
|
+
this.listViewInstance.removeSelect();
|
|
682
|
+
this.activeIndex = undefined;
|
|
683
|
+
}
|
|
684
|
+
if (!parseFloat(this.bottomElement.style.height) && !parseFloat(this.topElement.style.height)) {
|
|
685
|
+
this.updateUI(liItem, this.uiLastIndex, this.bottomElement);
|
|
686
|
+
} else if (parseFloat(this.bottomElement.style.height)) {
|
|
687
|
+
this.bottomElement.style.height = parseFloat(this.bottomElement.style.height) - this.listItemHeight + 'px';
|
|
688
|
+
this.updateUI(liItem, this.uiLastIndex, this.bottomElement);
|
|
689
|
+
} else {
|
|
690
|
+
this.topElement.style.height = parseFloat(this.topElement.style.height) - this.listItemHeight + 'px';
|
|
691
|
+
this.updateUI(liItem, (this.uiFirstIndex - 1), this.topElement.nextSibling as HTMLElement);
|
|
692
|
+
this.uiLastIndex--;
|
|
693
|
+
this.uiFirstIndex--;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
this.changeUiIndices(index, false);
|
|
698
|
+
this.listDiff = Math.round(parseFloat(this.topElement.style.height) / this.listItemHeight);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
private changeUiIndices(index: number, increment: boolean): void {
|
|
702
|
+
const keys: string[] = Object.keys(this.uiIndices);
|
|
703
|
+
for (let ind: number = 0; ind < keys.length; ind++) {
|
|
704
|
+
this.uiIndices[keys[ind]] = this.uiIndices[keys[ind]].map((i: number) => {
|
|
705
|
+
if (i >= index) {
|
|
706
|
+
return increment ? ++i : --i;
|
|
707
|
+
} else {
|
|
708
|
+
return i;
|
|
709
|
+
}
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
public addItem(data: DataSource[], fields: Fields, dataSource: DataSource[]): void {
|
|
715
|
+
for (let i: number = 0; i < data.length; i++) {
|
|
716
|
+
const currentItem: { [key: string]: object; } = data[i];
|
|
717
|
+
// push the given data to main data array
|
|
718
|
+
dataSource.push(currentItem);
|
|
719
|
+
// recalculate all the group data or other datasource related things
|
|
720
|
+
this.listViewInstance.setViewDataSource(dataSource);
|
|
721
|
+
// render list items for first time due to no datasource present earlier
|
|
722
|
+
if (!this.domItemCount) {
|
|
723
|
+
// fresh rendering for first time
|
|
724
|
+
if ((this.listViewInstance.template || this.listViewInstance.groupTemplate) && !this.isNgTemplate()) {
|
|
725
|
+
this.listViewInstance.listBaseOption.template = null;
|
|
726
|
+
this.listViewInstance.listBaseOption.groupTemplate = null;
|
|
727
|
+
this.listViewInstance.listBaseOption.itemCreated = this.createUIItem.bind(this);
|
|
728
|
+
}
|
|
729
|
+
this.uiVirtualization();
|
|
730
|
+
// when expected expected DOM count doesn't meet the condition we need to create and inject new item into DOM
|
|
731
|
+
} else if (this.domItemCount < this.expectedDomItemCount) {
|
|
732
|
+
const ds: DataSource = this.listViewInstance.findItemFromDS(dataSource, fields);
|
|
733
|
+
|
|
734
|
+
if (ds instanceof Array) {
|
|
735
|
+
if (this.listViewInstance.ulElement) {
|
|
736
|
+
let index: number = (this.listViewInstance.curViewDS as DataSource[]).indexOf(currentItem);
|
|
737
|
+
// inject new list item into DOM
|
|
738
|
+
this.createAndInjectNewItem(currentItem, index);
|
|
739
|
+
|
|
740
|
+
// check for group header item
|
|
741
|
+
const curViewDS: DataSource =
|
|
742
|
+
(this.listViewInstance.curViewDS as DataSource[])[index - 1];
|
|
743
|
+
if (curViewDS && curViewDS.isHeader && (curViewDS.items as DataSource[]).length === 1) {
|
|
744
|
+
// target group item index in datasource
|
|
745
|
+
--index;
|
|
746
|
+
// inject new group header into DOM for previously created list item
|
|
747
|
+
this.createAndInjectNewItem(curViewDS, index);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
// recollect all the list item into collection
|
|
751
|
+
this.listViewInstance.liCollection =
|
|
752
|
+
<HTMLElement[] & NodeListOf<HTMLLIElement>>this.listViewInstance.curUL.querySelectorAll('li');
|
|
753
|
+
}
|
|
754
|
+
} else {
|
|
755
|
+
const index: number = (this.listViewInstance.curViewDS as { [key: string]: any; }[]).indexOf(currentItem);
|
|
756
|
+
// virtually new add list item based on the scollbar position
|
|
757
|
+
this.addUiItem(index);
|
|
758
|
+
// check for group header item needs to be added
|
|
759
|
+
const curViewDS: DataSource = (this.listViewInstance.curViewDS as DataSource[])[index - 1];
|
|
760
|
+
if (curViewDS && curViewDS.isHeader && (curViewDS.items as DataSource[]).length === 1) {
|
|
761
|
+
this.addUiItem(index - 1);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
private createAndInjectNewItem(itemData: DataSource, index: number): void {
|
|
768
|
+
// generate li item for given datasource
|
|
769
|
+
let target: HTMLElement;
|
|
770
|
+
const li: HTMLElement[] = ListBase.createListItemFromJson(
|
|
771
|
+
this.listViewInstance.createElement,
|
|
772
|
+
// eslint-disable-next-line
|
|
773
|
+
[itemData] as any,
|
|
774
|
+
this.listViewInstance.listBaseOption,
|
|
775
|
+
null,
|
|
776
|
+
null,
|
|
777
|
+
this.listViewInstance);
|
|
778
|
+
// check for target element whether to insert before last item or group item
|
|
779
|
+
if ((Object.keys(this.listViewInstance.curViewDS).length - 1) === index) {
|
|
780
|
+
target = this.listViewInstance.curUL.lastElementChild as HTMLElement;
|
|
781
|
+
} else {
|
|
782
|
+
// target group header's first child item to append its header
|
|
783
|
+
target = this.listViewInstance.getLiFromObjOrElement(this.listViewInstance.curViewDS[index + 1]) ||
|
|
784
|
+
this.listViewInstance.getLiFromObjOrElement(this.listViewInstance.curViewDS[index + 2]);
|
|
785
|
+
}
|
|
786
|
+
if (this.listViewInstance.fields.groupBy && this.listViewInstance.curViewDS[index + 1] && this.listViewInstance.curViewDS[index + 1].isHeader) {
|
|
787
|
+
let targetEle : HTMLElement = this.listViewInstance.getLiFromObjOrElement(this.listViewInstance.curViewDS[index - 1]);
|
|
788
|
+
if (targetEle) {
|
|
789
|
+
target = targetEle.nextElementSibling as HTMLElement;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
// insert before the target element
|
|
793
|
+
this.listViewInstance.ulElement.insertBefore(li[0], target);
|
|
794
|
+
// increment internal DOM count, last index count for new element
|
|
795
|
+
this.domItemCount++;
|
|
796
|
+
if (this.bottomElementHeight <= 0) {
|
|
797
|
+
this.uiLastIndex++;
|
|
798
|
+
}
|
|
799
|
+
// recalculate the current item height, to avoid jumpy scroller
|
|
800
|
+
this.refreshItemHeight();
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
public createUIItem(args: ItemCreatedArgs): void {
|
|
804
|
+
const virtualTemplate: string = this.listViewInstance.template;
|
|
805
|
+
const template: HTMLElement = this.listViewInstance.createElement('div');
|
|
806
|
+
let commonTemplate: string = '<div class="e-text-content" role="presentation"> ' +
|
|
807
|
+
'<span class="e-list-text"> ${' + this.listViewInstance.fields.text + '} </span></div>';
|
|
808
|
+
if (this.listViewInstance.isReact) {
|
|
809
|
+
commonTemplate = null;
|
|
810
|
+
}
|
|
811
|
+
if (this.listViewInstance.showCheckBox) {
|
|
812
|
+
// eslint-disable-next-line
|
|
813
|
+
(this.listViewInstance as any).renderCheckbox(args);
|
|
814
|
+
if ((!isNullOrUndefined(this.listViewInstance.virtualCheckBox)) &&
|
|
815
|
+
(!isNullOrUndefined(this.listViewInstance.virtualCheckBox.outerHTML))) {
|
|
816
|
+
const div: HTMLElement = document.createElement('div');
|
|
817
|
+
div.innerHTML = this.listViewInstance.template || commonTemplate;
|
|
818
|
+
if (div.children && div.children[0]) {
|
|
819
|
+
div.children[0].classList.add('e-checkbox');
|
|
820
|
+
if (this.listViewInstance.checkBoxPosition === 'Left')
|
|
821
|
+
{
|
|
822
|
+
div.children[0].classList.add('e-checkbox-left');
|
|
823
|
+
}
|
|
824
|
+
else
|
|
825
|
+
{
|
|
826
|
+
div.children[0].classList.add('e-checkbox-right');
|
|
827
|
+
}
|
|
828
|
+
if (this.listViewInstance.checkBoxPosition === 'Left') {
|
|
829
|
+
div.children[0].insertBefore(this.listViewInstance.virtualCheckBox, (div.childNodes[0] as HTMLElement).children[0]);
|
|
830
|
+
} else {
|
|
831
|
+
div.children[0].appendChild(this.listViewInstance.virtualCheckBox);
|
|
832
|
+
}
|
|
833
|
+
this.listViewInstance.template = div.innerHTML;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
template.innerHTML = this.listViewInstance.template;
|
|
837
|
+
this.listViewInstance.template = virtualTemplate;
|
|
838
|
+
} else {
|
|
839
|
+
template.innerHTML = this.listViewInstance.template || commonTemplate;
|
|
840
|
+
}
|
|
841
|
+
// eslint-disable-next-line
|
|
842
|
+
const templateElements: any = template.getElementsByTagName('*');
|
|
843
|
+
const groupTemplate: HTMLElement = this.listViewInstance.createElement('div');
|
|
844
|
+
if (this.listViewInstance.fields.groupBy) {
|
|
845
|
+
groupTemplate.innerHTML = this.listViewInstance.groupTemplate || commonTemplate;
|
|
846
|
+
}
|
|
847
|
+
// eslint-disable-next-line
|
|
848
|
+
let groupTemplateElements: any = groupTemplate.getElementsByTagName('*');
|
|
849
|
+
if (args.curData.isHeader) {
|
|
850
|
+
this.headerData = args.curData;
|
|
851
|
+
}
|
|
852
|
+
this.templateData = args.curData.isHeader ? (args.curData as { [key: string]: any[]; }).items[0] as DataSource :
|
|
853
|
+
args.curData;
|
|
854
|
+
if (!this.listViewInstance.isReact || (typeof this.listViewInstance.template == "string" && !args.item.classList.contains("e-list-group-item")) ||
|
|
855
|
+
(typeof this.listViewInstance.groupTemplate == "string" && args.item.classList.contains("e-list-group-item"))) {
|
|
856
|
+
args.item.innerHTML = '';
|
|
857
|
+
}
|
|
858
|
+
(<ElementContext>args.item).context = { data: args.curData, nodes: { flatTemplateNodes: [], groupTemplateNodes: [] } };
|
|
859
|
+
for (let i: number = 0; i < templateElements.length; i++) {
|
|
860
|
+
this.compileTemplate(templateElements[i] as HTMLElement, args.item, false);
|
|
861
|
+
}
|
|
862
|
+
for (let i: number = 0; i < groupTemplateElements.length; i++) {
|
|
863
|
+
this.compileTemplate(groupTemplateElements[i] as HTMLElement, args.item, true);
|
|
864
|
+
}
|
|
865
|
+
(<ElementContext>args.item).context.template = args.curData.isHeader ? template.firstElementChild :
|
|
866
|
+
groupTemplate.firstElementChild;
|
|
867
|
+
(<ElementContext>args.item).context.type = args.curData.isHeader ? 'flatList' : 'groupList';
|
|
868
|
+
const element: HTMLElement = args.curData.isHeader ? groupTemplate : template;
|
|
869
|
+
if (element.firstElementChild) {
|
|
870
|
+
args.item.insertBefore(element.firstElementChild, null);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
private compileTemplate(element: HTMLElement, item: HTMLElement, isHeader: boolean): void {
|
|
875
|
+
this.textProperty(element, item, isHeader);
|
|
876
|
+
this.classProperty(element, item, isHeader);
|
|
877
|
+
this.attributeProperty(element, item, isHeader);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
private onChange(newData: DataSource, listElement: HTMLElement): void {
|
|
881
|
+
(<ElementContext>listElement).context.data = newData;
|
|
882
|
+
// eslint-disable-next-line max-len
|
|
883
|
+
const groupTemplateNodes: object[] = ((<ElementContext>listElement).context.nodes as { [key: string]: object[] }).groupTemplateNodes;
|
|
884
|
+
// eslint-disable-next-line max-len
|
|
885
|
+
const flatTemplateNodes: object[] = ((<ElementContext>listElement).context.nodes as { [key: string]: object[] }).flatTemplateNodes;
|
|
886
|
+
// eslint-disable-next-line
|
|
887
|
+
if (!isNullOrUndefined(newData.isHeader as any) && newData.isHeader as any && (<ElementContext>listElement).context.type as any === 'groupList' as any) {
|
|
888
|
+
// eslint-disable-next-line
|
|
889
|
+
const element: HTMLElement = listElement.firstElementChild as HTMLElement as any;
|
|
890
|
+
detach(listElement.firstElementChild);
|
|
891
|
+
listElement.insertBefore((<ElementContext>listElement).context.template as HTMLElement, null);
|
|
892
|
+
(<ElementContext>listElement).context.template = element;
|
|
893
|
+
(<ElementContext>listElement).context.type = 'flatList';
|
|
894
|
+
for (let i: number = 0; i < groupTemplateNodes.length; i++) {
|
|
895
|
+
// eslint-disable-next-line
|
|
896
|
+
(groupTemplateNodes[i] as { [key: string]: Function }).onChange(newData);
|
|
897
|
+
}
|
|
898
|
+
} else if (!newData.isHeader && (<ElementContext>listElement).context.type === 'flatList') {
|
|
899
|
+
const element: HTMLElement = listElement.firstElementChild as HTMLElement;
|
|
900
|
+
detach(listElement.firstElementChild);
|
|
901
|
+
listElement.insertBefore((<ElementContext>listElement).context.template as HTMLElement, null);
|
|
902
|
+
(<ElementContext>listElement).context.template = element;
|
|
903
|
+
(<ElementContext>listElement).context.type = 'groupList';
|
|
904
|
+
for (let i: number = 0; i < flatTemplateNodes.length; i++) {
|
|
905
|
+
// eslint-disable-next-line
|
|
906
|
+
(flatTemplateNodes[i] as { [key: string]: Function }).onChange(newData);
|
|
907
|
+
}
|
|
908
|
+
} else if (!newData.isHeader) {
|
|
909
|
+
for (let i: number = 0; i < flatTemplateNodes.length; i++) {
|
|
910
|
+
// eslint-disable-next-line
|
|
911
|
+
(flatTemplateNodes[i] as { [key: string]: Function }).onChange(newData);
|
|
912
|
+
}
|
|
913
|
+
} else {
|
|
914
|
+
for (let i: number = 0; i < groupTemplateNodes.length; i++) {
|
|
915
|
+
// eslint-disable-next-line
|
|
916
|
+
(groupTemplateNodes[i] as { [key: string]: Function }).onChange(newData);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// eslint-disable-next-line
|
|
922
|
+
private updateContextData(listElement: HTMLElement, node: { [key: string]: string | Function | string[] }, isHeader: Boolean): void {
|
|
923
|
+
if (isHeader) {
|
|
924
|
+
((<ElementContext>listElement).context.nodes as { [key: string]: object[] }).groupTemplateNodes.push(node);
|
|
925
|
+
} else {
|
|
926
|
+
((<ElementContext>listElement).context.nodes as { [key: string]: object[] }).flatTemplateNodes.push(node);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
private classProperty(element: HTMLElement, listElement: HTMLElement, isHeader: boolean): void {
|
|
930
|
+
const regex: RegExp = new RegExp('\\${([^}]*)}', 'g');
|
|
931
|
+
const resultantOutput: RegExpExecArray[] = [];
|
|
932
|
+
let regexMatch: RegExpExecArray;
|
|
933
|
+
while (regexMatch !== null) {
|
|
934
|
+
const match: RegExpExecArray = regex.exec(element.className);
|
|
935
|
+
resultantOutput.push(match);
|
|
936
|
+
regexMatch = match;
|
|
937
|
+
if (regexMatch === null) {
|
|
938
|
+
resultantOutput.pop();
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
if (resultantOutput && resultantOutput.length) {
|
|
942
|
+
for (let i: number = 0; i < resultantOutput.length; i++) {
|
|
943
|
+
const classNameMatch: RegExpExecArray = resultantOutput[i];
|
|
944
|
+
// eslint-disable-next-line
|
|
945
|
+
let classFunction: Function;
|
|
946
|
+
if (classNameMatch[1].indexOf('?') !== -1 && classNameMatch[1].indexOf(':') !== -1) {
|
|
947
|
+
// tslint:disable-next-line:no-function-constructor-with-string-args
|
|
948
|
+
classFunction = new Function('data', 'return ' + classNameMatch[1].replace(/\$/g, 'data.'));
|
|
949
|
+
} else {
|
|
950
|
+
// tslint:disable-next-line:no-function-constructor-with-string-args
|
|
951
|
+
classFunction = new Function('data', 'return ' + 'data.' + classNameMatch[1]);
|
|
952
|
+
}
|
|
953
|
+
// eslint-disable-next-line
|
|
954
|
+
const subNode: { [key: string]: string | Function } = {};
|
|
955
|
+
if (isHeader) {
|
|
956
|
+
subNode.bindedvalue = classFunction(this.headerData);
|
|
957
|
+
} else {
|
|
958
|
+
subNode.bindedvalue = classFunction(this.templateData);
|
|
959
|
+
}
|
|
960
|
+
subNode.onChange = (value: DataSource) => {
|
|
961
|
+
if (subNode.bindedvalue) {
|
|
962
|
+
removeClass([element], (subNode.bindedvalue as string).split(' ').filter((css: string) => css));
|
|
963
|
+
}
|
|
964
|
+
const newCss: string = classFunction(value) as string;
|
|
965
|
+
if (newCss) {
|
|
966
|
+
addClass([element], (newCss).split(' ').filter((css: string) => css));
|
|
967
|
+
}
|
|
968
|
+
subNode.bindedvalue = newCss;
|
|
969
|
+
};
|
|
970
|
+
const className: string[] = classNameMatch[0].split(' ');
|
|
971
|
+
for (let i: number = 0; i < className.length; i++) { element.classList.remove(className[i]); }
|
|
972
|
+
if (subNode.bindedvalue) {
|
|
973
|
+
addClass([element], (subNode.bindedvalue as string).split(' ').filter((css: string) => css));
|
|
974
|
+
}
|
|
975
|
+
this.updateContextData(listElement, subNode, isHeader);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
private attributeProperty(element: HTMLElement, listElement: HTMLElement, isHeader: boolean): void {
|
|
980
|
+
const attributeNames: string[] = [];
|
|
981
|
+
for (let i: number = 0; i < element.attributes.length; i++) {
|
|
982
|
+
attributeNames.push(element.attributes[i].nodeName);
|
|
983
|
+
}
|
|
984
|
+
if (attributeNames.indexOf('class') !== -1) {
|
|
985
|
+
attributeNames.splice(attributeNames.indexOf('class'), 1);
|
|
986
|
+
}
|
|
987
|
+
for (let i: number = 0; i < attributeNames.length; i++) {
|
|
988
|
+
const attributeName: string = attributeNames[i];
|
|
989
|
+
const attrNameMatch: RegExpExecArray | string[] = new RegExp('\\${([^}]*)}', 'g').exec(attributeName) || [];
|
|
990
|
+
const attrValueMatch: RegExpExecArray | string[] = new RegExp('\\${([^}]*)}', 'g').exec(element.getAttribute(attributeName))
|
|
991
|
+
|| [];
|
|
992
|
+
// eslint-disable-next-line
|
|
993
|
+
let attributeNameFunction: Function;
|
|
994
|
+
// eslint-disable-next-line
|
|
995
|
+
let attributeValueFunction: Function;
|
|
996
|
+
if (attrNameMatch.length || attrValueMatch.length) {
|
|
997
|
+
if (attrNameMatch[1]) {
|
|
998
|
+
// tslint:disable-next-line:no-function-constructor-with-string-args
|
|
999
|
+
attributeNameFunction = new Function('data', 'return ' + 'data.' + attrNameMatch[1]);
|
|
1000
|
+
}
|
|
1001
|
+
if (attrValueMatch[1]) {
|
|
1002
|
+
if (attrValueMatch[1].indexOf('?') !== -1 && attrValueMatch[1].indexOf(':') !== -1) {
|
|
1003
|
+
// tslint:disable-next-line:no-function-constructor-with-string-args
|
|
1004
|
+
attributeValueFunction = new Function('data', 'return ' + attrValueMatch[1].replace(/\$/g, 'data.'));
|
|
1005
|
+
} else {
|
|
1006
|
+
// tslint:disable-next-line:no-function-constructor-with-string-args
|
|
1007
|
+
attributeValueFunction = new Function('data', 'return ' + 'data.' + attrValueMatch[1]);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
1011
|
+
const subNode: { [key: string]: string[] | Function | string } = {};
|
|
1012
|
+
if (isHeader) {
|
|
1013
|
+
subNode.bindedvalue = [attrNameMatch[1] === undefined ? undefined : attributeNameFunction(this.headerData),
|
|
1014
|
+
attrValueMatch[1] === undefined ? undefined : attributeValueFunction(this.headerData)];
|
|
1015
|
+
} else {
|
|
1016
|
+
subNode.bindedvalue = [attrNameMatch[1] === undefined ? undefined : attributeNameFunction(this.templateData),
|
|
1017
|
+
attrValueMatch[1] === undefined ? undefined : attributeValueFunction(this.templateData)];
|
|
1018
|
+
}
|
|
1019
|
+
subNode.attrName = (subNode.bindedvalue as string[])[0] === undefined ?
|
|
1020
|
+
attributeName : (subNode.bindedvalue as string[])[0];
|
|
1021
|
+
subNode.onChange = (value: DataSource) => {
|
|
1022
|
+
const bindedvalue: string = (subNode.bindedvalue as string[])[1] === undefined ?
|
|
1023
|
+
element.getAttribute(subNode.attrName as string) : attributeValueFunction(value);
|
|
1024
|
+
element.removeAttribute(subNode.attrName as string);
|
|
1025
|
+
subNode.attrName = (subNode.bindedvalue as string[])[0] === undefined ? subNode.attrName : attributeNameFunction(value);
|
|
1026
|
+
element.setAttribute(subNode.attrName as string, bindedvalue);
|
|
1027
|
+
subNode.bindedvalue = [(subNode.bindedvalue as string[])[0] === undefined ? undefined : attributeNameFunction(value),
|
|
1028
|
+
(subNode.bindedvalue as string[])[1] === undefined ? undefined : attributeValueFunction(value)];
|
|
1029
|
+
};
|
|
1030
|
+
const attributeValue: string = (subNode.bindedvalue as string[])[1] === undefined ? element.getAttribute(attributeName) :
|
|
1031
|
+
(subNode.bindedvalue as string[])[1];
|
|
1032
|
+
element.removeAttribute(attributeName as string);
|
|
1033
|
+
element.setAttribute(subNode.attrName as string, attributeValue as string);
|
|
1034
|
+
this.updateContextData(listElement, subNode, isHeader);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
private textProperty(element: HTMLElement, listElement: HTMLElement, isHeader: boolean): void {
|
|
1041
|
+
const regex: RegExp = new RegExp('\\${([^}]*)}', 'g');
|
|
1042
|
+
const resultantOutput: RegExpExecArray[] = [];
|
|
1043
|
+
let regexMatch: RegExpExecArray;
|
|
1044
|
+
while (regexMatch !== null) {
|
|
1045
|
+
const match: RegExpExecArray = regex.exec(element.innerText);
|
|
1046
|
+
resultantOutput.push(match);
|
|
1047
|
+
regexMatch = match;
|
|
1048
|
+
if (regexMatch === null) {
|
|
1049
|
+
resultantOutput.pop();
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
const isChildHasTextContent: boolean = Array.prototype.some.call(element.children, (element: HTMLElement) => {
|
|
1053
|
+
if (new RegExp('\\${([^}]*)}', 'g').exec(element.innerText)) {
|
|
1054
|
+
return true;
|
|
1055
|
+
|
|
1056
|
+
} else {
|
|
1057
|
+
return false;
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
if (resultantOutput && resultantOutput.length && !isChildHasTextContent) {
|
|
1061
|
+
for (let i: number = 0; i < resultantOutput.length; i++) {
|
|
1062
|
+
const textPropertyMatch: RegExpExecArray = resultantOutput[i];
|
|
1063
|
+
// eslint-disable-next-line
|
|
1064
|
+
const subNode: { [key: string]: string | Function } = {};
|
|
1065
|
+
// eslint-disable-next-line
|
|
1066
|
+
let textFunction: Function;
|
|
1067
|
+
if (textPropertyMatch[1].indexOf('?') !== -1 && textPropertyMatch[1].indexOf(':') !== -1) {
|
|
1068
|
+
// tslint:disable-next-line:no-function-constructor-with-string-args
|
|
1069
|
+
textFunction = new Function('data', 'return ' + textPropertyMatch[1].replace(/\$/g, 'data.'));
|
|
1070
|
+
} else {
|
|
1071
|
+
// tslint:disable-next-line:no-function-constructor-with-string-args
|
|
1072
|
+
textFunction = new Function('data', 'return ' + 'data.' + textPropertyMatch[1]);
|
|
1073
|
+
}
|
|
1074
|
+
if (isHeader) {
|
|
1075
|
+
subNode.bindedvalue = textFunction(this.headerData);
|
|
1076
|
+
} else {
|
|
1077
|
+
subNode.bindedvalue = textFunction(this.templateData);
|
|
1078
|
+
}
|
|
1079
|
+
subNode.onChange = (value: DataSource) => {
|
|
1080
|
+
element.innerText = element.innerText.replace(subNode.bindedvalue as string, textFunction(value));
|
|
1081
|
+
subNode.bindedvalue = textFunction(value);
|
|
1082
|
+
};
|
|
1083
|
+
element.innerText = element.innerText.replace(textPropertyMatch[0], subNode.bindedvalue as string);
|
|
1084
|
+
this.updateContextData(listElement, subNode, isHeader);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
public reRenderUiVirtualization(): void {
|
|
1090
|
+
this.wireScrollEvent(true);
|
|
1091
|
+
if (this.listViewInstance.contentContainer) {
|
|
1092
|
+
detach(this.listViewInstance.contentContainer);
|
|
1093
|
+
}
|
|
1094
|
+
this.listViewInstance.preRender();
|
|
1095
|
+
// resetting the dom count to 0, to avoid edge case of dataSource suddenly becoming zero
|
|
1096
|
+
// and then manually adding item using addItem API
|
|
1097
|
+
this.domItemCount = 0;
|
|
1098
|
+
this.listViewInstance.header();
|
|
1099
|
+
this.listViewInstance.setLocalData();
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
private updateUI(element: HTMLElement, index: number, targetElement?: HTMLElement): void {
|
|
1103
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
1104
|
+
const onChange: Function = this.isNgTemplate() ? this.onNgChange : this.onChange;
|
|
1105
|
+
if (this.listViewInstance.template || this.listViewInstance.groupTemplate) {
|
|
1106
|
+
const curViewDS: DataSource = (this.listViewInstance.curViewDS as DataSource[])[index];
|
|
1107
|
+
// eslint-disable-next-line
|
|
1108
|
+
element.dataset.uid = (curViewDS[this.listViewInstance.fields.id]) as any ?
|
|
1109
|
+
// eslint-disable-next-line
|
|
1110
|
+
(curViewDS[this.listViewInstance.fields.id]) as any : ListBase.generateId() as any;
|
|
1111
|
+
onChange(curViewDS, element, this);
|
|
1112
|
+
} else {
|
|
1113
|
+
this.updateUiContent(element, index);
|
|
1114
|
+
}
|
|
1115
|
+
this.changeElementAttributes(element, index);
|
|
1116
|
+
if (targetElement) {
|
|
1117
|
+
this.listViewInstance.ulElement.insertBefore(element, targetElement);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
private onNgChange(newData: DataSource, listElement: ElementContext, virtualThis: Virtualization): void {
|
|
1122
|
+
// compile given target element with template for new data
|
|
1123
|
+
// eslint-disable-next-line
|
|
1124
|
+
const templateCompiler: Function = compile(virtualThis.listViewInstance.template);
|
|
1125
|
+
const resultElement: NodeList = templateCompiler(newData);
|
|
1126
|
+
while (listElement.lastChild) {
|
|
1127
|
+
listElement.removeChild(listElement.lastChild);
|
|
1128
|
+
}
|
|
1129
|
+
listElement.appendChild(resultElement[0]);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
public getModuleName(): string {
|
|
1133
|
+
return 'virtualization';
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
public destroy(): void {
|
|
1137
|
+
this.wireScrollEvent(true);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
interface DataSource {
|
|
1142
|
+
[key: string]: object;
|
|
1143
|
+
}
|
|
1144
|
+
|