Skip to content

Commit adfaf19

Browse files
committed
feat(treegrid): rework text highlight directive to be shared between grids #2530
1 parent 4f7ff9b commit adfaf19

10 files changed

+244
-497
lines changed

Diff for: projects/igniteui-angular/src/lib/directives/text-highlight/text-highlight.directive.spec.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ describe('IgxHighlight', () => {
2929
expect(component.highlight.value).toBe(component.html);
3030
expect(component.highlight.row).toBe(0);
3131
expect(component.highlight.column).toBe(0);
32-
expect(component.highlight.page).toBe(0);
3332
expect(component.highlight.containerClass).toBe('test');
3433
});
3534

@@ -279,7 +278,7 @@ describe('IgxHighlight', () => {
279278
@Component({
280279
template:
281280
// tslint:disable-next-line:max-line-length
282-
`<div igxTextHighlight [cssClass]="highlightClass" [activeCssClass]="activeHighlightClass" [groupName]="groupName" [value]="html" [column]="0" [row]="0" [page]="0" [containerClass]="'test'">
281+
`<div igxTextHighlight [cssClass]="highlightClass" [activeCssClass]="activeHighlightClass" [groupName]="groupName" [value]="html" [column]="0" [row]="0" [containerClass]="'test'">
283282
{{html}}
284283
</div>`
285284
})
@@ -308,9 +307,8 @@ class HighlightLoremIpsumComponent {
308307

309308
public activate(index: number) {
310309
const activeHighlightInfo: IActiveHighlightInfo = {
311-
rowIndex: 0,
312-
columnIndex: 0,
313-
page: 0,
310+
rowID: 0,
311+
columnID: 0,
314312
index: index
315313
};
316314
IgxTextHighlightDirective.setActiveHighlight(this.groupName, activeHighlightInfo);

Diff for: projects/igniteui-angular/src/lib/directives/text-highlight/text-highlight.directive.ts

+14-29
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ interface ISearchInfo {
2121
}
2222

2323
export interface IActiveHighlightInfo {
24-
rowIndex: number;
25-
columnIndex: number;
26-
page: number;
24+
rowID: any;
25+
columnID: any;
2726
index: number;
2827
}
2928

@@ -121,7 +120,7 @@ export class IgxTextHighlightDirective implements AfterViewInit, OnDestroy, OnCh
121120
}
122121

123122
/**
124-
* The index of the row on which the directive is currently on.
123+
* The identifier of the row on which the directive is currently on.
125124
*
126125
* ```html
127126
* <div
@@ -131,10 +130,10 @@ export class IgxTextHighlightDirective implements AfterViewInit, OnDestroy, OnCh
131130
* ```
132131
*/
133132
@Input('row')
134-
public row: number;
133+
public row: any;
135134

136135
/**
137-
* The index of the column on which the directive is currently on.
136+
* The identifier of the column on which the directive is currently on.
138137
*
139138
* ```html
140139
* <div
@@ -144,21 +143,7 @@ export class IgxTextHighlightDirective implements AfterViewInit, OnDestroy, OnCh
144143
* ```
145144
*/
146145
@Input('column')
147-
public column: number;
148-
149-
/**
150-
* The index of the page on which the directive is currently on.
151-
* It is used when the component containing the directive supports paging.
152-
*
153-
* ```html
154-
* <div
155-
* igxTextHighlight
156-
* [page]="0">
157-
* </div>
158-
* ```
159-
*/
160-
@Input('page')
161-
public page: number;
146+
public column: any;
162147

163148
/**
164149
* @hidden
@@ -181,9 +166,8 @@ export class IgxTextHighlightDirective implements AfterViewInit, OnDestroy, OnCh
181166
*/
182167
public static clearActiveHighlight(groupName) {
183168
IgxTextHighlightDirective.highlightGroupsMap.set(groupName, {
184-
rowIndex: -1,
185-
columnIndex: -1,
186-
page: -1,
169+
rowID: null,
170+
columnID: null,
187171
index: -1
188172
});
189173
IgxTextHighlightDirective.onActiveElementChanged.emit(groupName);
@@ -237,9 +221,8 @@ export class IgxTextHighlightDirective implements AfterViewInit, OnDestroy, OnCh
237221
ngAfterViewInit() {
238222
if (IgxTextHighlightDirective.highlightGroupsMap.has(this.groupName) === false) {
239223
IgxTextHighlightDirective.highlightGroupsMap.set(this.groupName, {
240-
rowIndex: -1,
241-
columnIndex: -1,
242-
page: -1,
224+
rowID: null,
225+
columnID: null,
243226
index: -1
244227
});
245228
}
@@ -299,7 +282,7 @@ export class IgxTextHighlightDirective implements AfterViewInit, OnDestroy, OnCh
299282
*/
300283
public activateIfNecessary(): void {
301284
const group = IgxTextHighlightDirective.highlightGroupsMap.get(this.groupName);
302-
if (group.columnIndex === this.column && group.rowIndex === this.row && group.page === this.page) {
285+
if (group.columnID === this.column && group.rowID === this.row) {
303286
this.activate(group.index);
304287
}
305288
}
@@ -447,7 +430,9 @@ export class IgxTextHighlightDirective implements AfterViewInit, OnDestroy, OnCh
447430

448431
private appendDiv() {
449432
this._div = this.renderer.createElement('div');
450-
this.renderer.addClass(this._div, this.containerClass);
433+
if ( this.containerClass) {
434+
this.renderer.addClass(this._div, this.containerClass);
435+
}
451436
this.renderer.appendChild(this.parentElement, this._div);
452437
}
453438

Diff for: projects/igniteui-angular/src/lib/grid-common/api-service.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { IgxColumnComponent } from './column.component';
44
import { QueryList } from '@angular/core';
55
import { IgxRowComponent } from './row.component';
66

7-
fdescribe('API Service - unit tests', () => {
7+
describe('API Service - unit tests', () => {
88
let service: MockGridService;
99
let grid: MockGrid;
1010

Diff for: projects/igniteui-angular/src/lib/grid-common/api.service.ts

+211-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { Injectable } from '@angular/core';
22
import { Subject } from 'rxjs';
33
import { cloneArray } from '../core/utils';
4-
import { IFilteringExpression, FilteringLogic } from '../data-operations/filtering-expression.interface';
4+
import { IFilteringExpression } from '../data-operations/filtering-expression.interface';
55
import { ISortingExpression, SortingDirection } from '../data-operations/sorting-expression.interface';
66
import { IgxGridCellComponent } from './cell.component';
77
import { IgxColumnComponent } from './column.component';
88
import { IgxRowComponent } from './row.component';
99
import { IFilteringOperation, FilteringExpressionsTree, IFilteringExpressionsTree } from '../../public_api';
1010
import { IGridEditEventArgs, IGridComponent } from './grid-interfaces';
11+
import { IgxTextHighlightDirective } from '../directives/text-highlight/text-highlight.directive';
12+
import { IgxForOfDirective } from '../directives/for-of/for_of.directive';
1113

1214
/**
1315
*@hidden
@@ -347,12 +349,218 @@ export class IGridAPIService <T extends IGridComponent> {
347349
});
348350
}
349351

350-
protected remove_grouping_expression(id: string, fieldName: string) {
352+
public refreshSearch(id: string, updateActiveInfo?: boolean) {
353+
const grid = this.get(id);
354+
if (grid && grid.lastSearchInfo.searchText) {
355+
this.rebuildMatchCache(id);
356+
357+
if (updateActiveInfo) {
358+
const activeInfo = IgxTextHighlightDirective.highlightGroupsMap.get(id);
359+
grid.lastSearchInfo.matchInfoCache.forEach((match, i) => {
360+
if (match.column === activeInfo.columnID &&
361+
match.row === activeInfo.rowID &&
362+
match.index === activeInfo.index) {
363+
grid.lastSearchInfo.activeMatchIndex = i;
364+
}
365+
});
366+
}
367+
368+
return this.find(
369+
id,
370+
grid.lastSearchInfo.searchText,
371+
0,
372+
grid.lastSearchInfo.caseSensitive,
373+
grid.lastSearchInfo.exactMatch,
374+
false
375+
);
376+
} else {
377+
return 0;
378+
}
379+
}
380+
381+
public find(id: string, text: string, increment: number, caseSensitive?: boolean, exactMatch?: boolean, scroll?: boolean) {
382+
const grid = this.get(id);
383+
if (!grid || !grid.rowList) {
384+
return 0;
385+
}
386+
387+
const editModeCell = this.get_cell_inEditMode(id);
388+
if (editModeCell) {
389+
this.escape_editMode(id);
390+
}
391+
392+
if (!text) {
393+
this.clearSearch(id);
394+
return 0;
395+
}
396+
397+
const caseSensitiveResolved = caseSensitive ? true : false;
398+
const exactMatchResolved = exactMatch ? true : false;
399+
let rebuildCache = false;
400+
401+
if (grid.lastSearchInfo.searchText !== text ||
402+
grid.lastSearchInfo.caseSensitive !== caseSensitiveResolved ||
403+
grid.lastSearchInfo.exactMatch !== exactMatchResolved) {
404+
grid.lastSearchInfo = {
405+
searchText: text,
406+
activeMatchIndex: 0,
407+
caseSensitive: caseSensitiveResolved,
408+
exactMatch: exactMatchResolved,
409+
matchInfoCache: []
410+
};
411+
412+
rebuildCache = true;
413+
} else {
414+
grid.lastSearchInfo.activeMatchIndex += increment;
415+
}
416+
417+
if (rebuildCache) {
418+
grid.dataRowList.forEach((row) => {
419+
row.cells.forEach((c) => {
420+
c.highlightText(text, caseSensitiveResolved, exactMatchResolved);
421+
});
422+
});
423+
424+
this.rebuildMatchCache(id);
425+
}
426+
427+
if (grid.lastSearchInfo.activeMatchIndex >= grid.lastSearchInfo.matchInfoCache.length) {
428+
grid.lastSearchInfo.activeMatchIndex = 0;
429+
} else if (grid.lastSearchInfo.activeMatchIndex < 0) {
430+
grid.lastSearchInfo.activeMatchIndex = grid.lastSearchInfo.matchInfoCache.length - 1;
431+
}
432+
433+
if (grid.lastSearchInfo.matchInfoCache.length) {
434+
const matchInfo = grid.lastSearchInfo.matchInfoCache[grid.lastSearchInfo.activeMatchIndex];
435+
436+
IgxTextHighlightDirective.setActiveHighlight(id, {
437+
columnID: matchInfo.column,
438+
rowID: matchInfo.row,
439+
index: matchInfo.index,
440+
});
441+
442+
if (scroll !== false) {
443+
this.scrollTo(id, matchInfo.row, matchInfo.column);
444+
}
445+
} else {
446+
IgxTextHighlightDirective.clearActiveHighlight(id);
447+
}
448+
449+
return grid.lastSearchInfo.matchInfoCache.length;
450+
}
451+
452+
public clearSearch(id: string) {
453+
this.get(id).lastSearchInfo = {
454+
searchText: '',
455+
caseSensitive: false,
456+
exactMatch: false,
457+
activeMatchIndex: 0,
458+
matchInfoCache: []
459+
};
460+
461+
this.get(id).dataRowList.forEach((row) => {
462+
row.cells.forEach((c) => {
463+
c.clearHighlight();
464+
});
465+
});
466+
}
467+
468+
protected scrollTo(id: string, row: any, column: any): void {
469+
const grid = this.get(id);
470+
const rowIndex = grid.filteredSortedData.indexOf(row);
471+
let columnIndex = this.get_column_by_name(id, column).visibleIndex;
472+
473+
if (grid.paging) {
474+
grid.page = Math.floor(rowIndex / grid.perPage);
475+
}
351476

477+
this.scrollDirective(id, grid.verticalScrollContainer, rowIndex);
478+
479+
const scrollRow = grid.rowList.find(r => r.virtDirRow);
480+
const virtDir = scrollRow ? scrollRow.virtDirRow : null;
481+
482+
if (grid.pinnedColumns.length) {
483+
if (columnIndex >= grid.pinnedColumns.length) {
484+
columnIndex -= grid.pinnedColumns.length;
485+
this.scrollDirective(id, virtDir, columnIndex);
486+
}
487+
} else {
488+
this.scrollDirective(id, virtDir, columnIndex);
489+
}
352490
}
353491

354-
public refreshSearch(id: string, updateHighlight?: boolean) {
492+
private scrollDirective(id: string, directive: IgxForOfDirective<any>, goal: number): void {
493+
if (!directive) {
494+
return;
495+
}
496+
const grid = this.get(id);
497+
const state = directive.state;
498+
const start = state.startIndex;
499+
const isColumn = directive.igxForScrollOrientation === 'horizontal';
500+
501+
const size = directive.getItemCountInView();
502+
503+
if (start >= goal) {
504+
// scroll so that goal is at beggining of visible chunk
505+
directive.scrollTo(goal);
506+
} else if (start + size <= goal) {
507+
// scroll so that goal is at end of visible chunk
508+
if (isColumn) {
509+
directive.getHorizontalScroll().scrollLeft =
510+
directive.getColumnScrollLeft(goal) -
511+
parseInt(directive.igxForContainerSize, 10) +
512+
parseInt(grid.columns[goal].width, 10);
513+
} else {
514+
directive.scrollTo(goal - size + 1);
515+
}
516+
}
517+
}
355518

519+
private rebuildMatchCache(id: string) {
520+
const grid = this.get(id);
521+
grid.lastSearchInfo.matchInfoCache = [];
522+
523+
const caseSensitive = grid.lastSearchInfo.caseSensitive;
524+
const exactMatch = grid.lastSearchInfo.exactMatch;
525+
const searchText = caseSensitive ? grid.lastSearchInfo.searchText : grid.lastSearchInfo.searchText.toLowerCase();
526+
const data = grid.filteredSortedData;
527+
const columnItems = grid.visibleColumns.filter((c) => !c.columnGroup).sort((c1, c2) => c1.visibleIndex - c2.visibleIndex);
528+
529+
data.forEach((dataRow) => {
530+
columnItems.forEach((c) => {
531+
const value = c.formatter ? c.formatter(dataRow[c.field]) : dataRow[c.field];
532+
if (value !== undefined && value !== null && c.searchable) {
533+
let searchValue = caseSensitive ? String(value) : String(value).toLowerCase();
534+
535+
if (exactMatch) {
536+
if (searchValue === searchText) {
537+
grid.lastSearchInfo.matchInfoCache.push({
538+
row: dataRow,
539+
column: c.field,
540+
index: 0,
541+
});
542+
}
543+
} else {
544+
let occurenceIndex = 0;
545+
let searchIndex = searchValue.indexOf(searchText);
546+
547+
while (searchIndex !== -1) {
548+
grid.lastSearchInfo.matchInfoCache.push({
549+
row: dataRow,
550+
column: c.field,
551+
index: occurenceIndex++,
552+
});
553+
554+
searchValue = searchValue.substring(searchIndex + searchText.length);
555+
searchIndex = searchValue.indexOf(searchText);
556+
}
557+
}
558+
}
559+
});
560+
});
356561
}
357562

563+
protected remove_grouping_expression(id: string, fieldName: string) {
564+
565+
}
358566
}

Diff for: projects/igniteui-angular/src/lib/grid-common/cell.component.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<ng-template #defaultCell igxTextHighlight [cssClass]="highlightClass" [activeCssClass]="activeHighlightClass" [groupName]="gridID"
2-
[value]="formatter ? formatter(value) : value" [row]="rowIndex" [column]="this.column.visibleIndex" [page]="this.grid.page" [containerClass]="'igx-grid__td-text'">
2+
[value]="formatter ? formatter(value) : value" [row]="row.rowData" [column]="column.field" [containerClass]="'igx-grid__td-text'">
33
<div class="igx-grid__td-text">{{ formatter ? formatter(value) : value }}</div>
44
</ng-template>
55
<ng-template #inlineEditor let-cell="cell">

0 commit comments

Comments
 (0)