1- import { IgxGridNavigationService } from '../grid-navigation.service' ;
1+ import { IActiveNode , IgxGridNavigationService } from '../grid-navigation.service' ;
22import { Injectable } from '@angular/core' ;
33import { IgxPivotGridComponent } from './pivot-grid.component' ;
44import { HEADER_KEYS } from '../../core/utils' ;
5+ import { IgxPivotRowDimensionMrlRowComponent } from './pivot-row-dimension-mrl-row.component' ;
6+ import { IMultiRowLayoutNode } from '../public_api' ;
57
68@Injectable ( )
79export class IgxPivotGridNavigationService extends IgxGridNavigationService {
810 public override grid : IgxPivotGridComponent ;
11+ protected _isRowHeaderActive = false ;
12+ protected _isRowDimensionHeaderActive = false ;
913
10- public isRowHeaderActive : boolean ;
14+ public set isRowHeaderActive ( value : boolean ) {
15+ this . _isRowHeaderActive = value ;
16+ }
17+ public get isRowHeaderActive ( ) {
18+ return this . _isRowHeaderActive ;
19+ }
20+
21+ public set isRowDimensionHeaderActive ( value : boolean ) {
22+ this . _isRowDimensionHeaderActive = value ;
23+ }
24+ public get isRowDimensionHeaderActive ( ) {
25+ return this . _isRowDimensionHeaderActive ;
26+ }
1127
1228 public get lastRowDimensionsIndex ( ) {
13- return this . grid . rowDimensions . length - 1 ;
29+ return this . grid . visibleRowDimensions . length - 1 ;
1430 }
1531
1632 public focusOutRowHeader ( ) {
1733 this . isRowHeaderActive = false ;
34+ this . isRowDimensionHeaderActive = false ;
1835 }
1936
2037 public override handleNavigation ( event : KeyboardEvent ) {
@@ -26,9 +43,101 @@ export class IgxPivotGridNavigationService extends IgxGridNavigationService {
2643 }
2744 event . preventDefault ( ) ;
2845
29- const newActiveNode = {
30- row : this . activeNode . row , column : this . activeNode . column , level : null ,
46+ const newActiveNode : IActiveNode = {
47+ row : this . activeNode . row ,
48+ column : this . activeNode . column ,
49+ level : null ,
3150 mchCache : null ,
51+ layout : this . activeNode . layout
52+ }
53+
54+ let verticalContainer ;
55+ if ( this . grid . horizontalRowDimensions ) {
56+ let newPosition = {
57+ row : this . activeNode . row ,
58+ column : this . activeNode . column ,
59+ layout : this . activeNode . layout
60+ } ;
61+ verticalContainer = this . grid . verticalRowDimScrollContainers . first ;
62+ if ( key . includes ( 'left' ) ) {
63+ newPosition = this . getNextHorizontalPosition ( true , ctrl ) ;
64+ }
65+ if ( key . includes ( 'right' ) ) {
66+ newPosition = this . getNextHorizontalPosition ( false , ctrl ) ;
67+ }
68+ if ( key . includes ( 'up' ) || key === 'home' ) {
69+ newPosition = this . getNextVerticalPosition ( true , ctrl || key === 'home' , key === 'home' ) ;
70+ }
71+
72+ if ( key . includes ( 'down' ) || key === 'end' ) {
73+ newPosition = this . getNextVerticalPosition ( false , ctrl || key === 'end' , key === 'end' ) ;
74+ }
75+
76+ newActiveNode . row = newPosition . row ;
77+ newActiveNode . column = newPosition . column ;
78+ newActiveNode . layout = newPosition . layout ;
79+ } else {
80+ if ( ( key . includes ( 'left' ) || key === 'home' ) && this . activeNode . column > 0 ) {
81+ newActiveNode . column = ctrl || key === 'home' ? 0 : this . activeNode . column - 1 ;
82+ }
83+ if ( ( key . includes ( 'right' ) || key === 'end' ) && this . activeNode . column < this . lastRowDimensionsIndex ) {
84+ newActiveNode . column = ctrl || key === 'end' ? this . lastRowDimensionsIndex : this . activeNode . column + 1 ;
85+ }
86+
87+ verticalContainer = this . grid . verticalRowDimScrollContainers . toArray ( ) [ newActiveNode . column ] ;
88+ if ( key . includes ( 'up' ) && this . activeNode . row > 0 ) {
89+ newActiveNode . row = ctrl ? 0 : this . activeNode . row - 1 ;
90+ } else if ( key . includes ( 'up' ) ) {
91+ newActiveNode . row = 0 ;
92+ newActiveNode . column = newActiveNode . layout . colStart - 1 ;
93+ newActiveNode . layout = null ;
94+ this . isRowDimensionHeaderActive = true ;
95+ this . isRowHeaderActive = false ;
96+ this . grid . theadRow . nativeElement . focus ( ) ;
97+ }
98+
99+ if ( key . includes ( 'down' ) && this . activeNode . row < this . findLastDataRowIndex ( ) ) {
100+ newActiveNode . row = ctrl ? verticalContainer . igxForOf . length - 1 : Math . min ( this . activeNode . row + 1 , verticalContainer . igxForOf . length - 1 ) ;
101+ }
102+
103+ if ( key . includes ( 'left' ) || key . includes ( 'right' ) ) {
104+ const prevRIndex = this . activeNode . row ;
105+ const prevScrContainer = this . grid . verticalRowDimScrollContainers . toArray ( ) [ this . activeNode . column ] ;
106+ const src = prevScrContainer . getScrollForIndex ( prevRIndex ) ;
107+ newActiveNode . row = this . activeNode . mchCache && this . activeNode . mchCache . level === newActiveNode . column ?
108+ this . activeNode . mchCache . visibleIndex :
109+ verticalContainer . getIndexAtScroll ( src ) ;
110+ newActiveNode . mchCache = {
111+ visibleIndex : this . activeNode . row ,
112+ level : this . activeNode . column
113+ } ;
114+ }
115+ }
116+
117+ this . setActiveNode ( newActiveNode ) ;
118+ if ( verticalContainer . isIndexOutsideView ( newActiveNode . row ) ) {
119+ verticalContainer . scrollTo ( newActiveNode . row ) ;
120+ }
121+ } else {
122+ super . handleNavigation ( event ) ;
123+ }
124+ }
125+
126+ public override headerNavigation ( event : KeyboardEvent ) {
127+ const key = event . key . toLowerCase ( ) ;
128+ const ctrl = event . ctrlKey ;
129+ if ( ! HEADER_KEYS . has ( key ) ) {
130+ return ;
131+ }
132+
133+ if ( this . isRowDimensionHeaderActive ) {
134+ event . preventDefault ( ) ;
135+
136+ const newActiveNode : IActiveNode = {
137+ row : this . activeNode . row ,
138+ column : this . activeNode . column ,
139+ level : null ,
140+ mchCache : this . activeNode . mchCache ,
32141 layout : null
33142 }
34143
@@ -37,33 +146,55 @@ export class IgxPivotGridNavigationService extends IgxGridNavigationService {
37146 }
38147 if ( ( key . includes ( 'right' ) || key === 'end' ) && this . activeNode . column < this . lastRowDimensionsIndex ) {
39148 newActiveNode . column = ctrl || key === 'end' ? this . lastRowDimensionsIndex : this . activeNode . column + 1 ;
40- }
41- const verticalContainer = this . grid . verticalRowDimScrollContainers . toArray ( ) [ newActiveNode . column ] ;
42- if ( ( key . includes ( 'up' ) ) && this . activeNode . row > 0 ) {
43- newActiveNode . row = ctrl ? 0 : this . activeNode . row - 1 ;
44- }
45- if ( ( key . includes ( 'down' ) ) && this . activeNode . row < this . findLastDataRowIndex ( ) ) {
46- newActiveNode . row = ctrl ? verticalContainer . igxForOf . length - 1 : Math . min ( this . activeNode . row + 1 , verticalContainer . igxForOf . length - 1 ) ;
149+ } else if ( key . includes ( 'right' ) ) {
150+ this . isRowDimensionHeaderActive = false ;
151+ newActiveNode . column = 0 ;
152+ newActiveNode . level = this . activeNode . mchCache ?. level || 0 ;
153+ newActiveNode . mchCache = this . activeNode . mchCache || {
154+ level : 0 ,
155+ visibleIndex : 0
156+ } ;
47157 }
48158
49- if ( key . includes ( 'left' ) || key . includes ( 'right' ) ) {
50- const prevRIndex = this . activeNode . row ;
51- const prevScrContainer = this . grid . verticalRowDimScrollContainers . toArray ( ) [ this . activeNode . column ] ;
52- const src = prevScrContainer . getScrollForIndex ( prevRIndex ) ;
53- newActiveNode . row = this . activeNode . mchCache && this . activeNode . mchCache . level === newActiveNode . column ?
54- this . activeNode . mchCache . visibleIndex :
55- verticalContainer . getIndexAtScroll ( src ) ;
56- newActiveNode . mchCache = {
57- visibleIndex : this . activeNode . row ,
58- level : this . activeNode . column
59- } ;
159+ if ( key . includes ( 'down' ) ) {
160+ if ( this . grid . horizontalRowDimensions ) {
161+ this . activeNode . row = 0 ;
162+ this . activeNode . layout = {
163+ rowStart : 1 ,
164+ rowEnd : 2 ,
165+ colStart : newActiveNode . column + 1 ,
166+ colEnd : newActiveNode . column + 2 ,
167+ columnVisibleIndex : newActiveNode . column
168+ } ;
169+
170+ const newPosition = this . getNextVerticalPosition ( true , ctrl || key === 'home' , key === 'home' ) ;
171+ newActiveNode . row = 0 ;
172+ newActiveNode . column = newPosition . column ;
173+ newActiveNode . layout = newPosition . layout ;
174+ } else {
175+ const verticalContainer = this . grid . verticalRowDimScrollContainers . toArray ( ) [ newActiveNode . column ] ;
176+ newActiveNode . row = ctrl ? verticalContainer . igxForOf . length - 1 : 0 ;
177+ }
178+
179+ this . isRowDimensionHeaderActive = false ;
180+ this . isRowHeaderActive = true ;
181+ this . grid . rowDimensionContainer . toArray ( ) [ this . grid . horizontalRowDimensions ? 0 : newActiveNode . column ] . nativeElement . focus ( ) ;
60182 }
183+
61184 this . setActiveNode ( newActiveNode ) ;
62- if ( verticalContainer . isIndexOutsideView ( newActiveNode . row ) ) {
63- verticalContainer . scrollTo ( newActiveNode . row ) ;
185+ } else if ( key . includes ( 'left' ) && this . activeNode . column === 0 ) {
186+ this . isRowDimensionHeaderActive = true ;
187+ const newActiveNode : IActiveNode = {
188+ row : this . activeNode . row ,
189+ column : this . lastRowDimensionsIndex ,
190+ level : null ,
191+ mchCache : this . activeNode . mchCache ,
192+ layout : null
64193 }
194+
195+ this . setActiveNode ( newActiveNode ) ;
65196 } else {
66- super . handleNavigation ( event ) ;
197+ super . headerNavigation ( event ) ;
67198 }
68199 }
69200
@@ -74,4 +205,81 @@ export class IgxPivotGridNavigationService extends IgxGridNavigationService {
74205 super . focusTbody ( event ) ;
75206 }
76207 }
208+
209+ public getNextVerticalPosition ( previous , ctrl , homeEnd ) {
210+ const parentRow = this . grid . rowDimensionMrlRowsCollection . toArray ( ) [ this . activeNode . row ] ;
211+ const maxRowEnd = parentRow . rowGroup . length + 1 ;
212+ const curCellLayout = this . getNextVerticalColumnIndex ( parentRow , this . activeNode . layout . rowStart , this . activeNode . layout . colStart ) ;
213+ const nextBlock = ( previous && curCellLayout . rowStart === 1 ) || ( ! previous && curCellLayout . rowEnd === maxRowEnd ) ;
214+ if ( nextBlock &&
215+ ( ( previous && this . activeNode . row === 0 ) ||
216+ ( ! previous && this . activeNode . row === this . grid . rowDimensionMrlRowsCollection . length - 1 ) ) ) {
217+ if ( previous && this . grid . pivotUI . showRowHeaders ) {
218+ this . isRowDimensionHeaderActive = true ;
219+ this . isRowHeaderActive = false ;
220+ this . grid . theadRow . nativeElement . focus ( ) ;
221+ return { row : - 1 , column : this . activeNode . layout . colStart - 1 , layout : this . activeNode . layout } ;
222+ }
223+ return { row : this . activeNode . row , column : this . activeNode . column , layout : this . activeNode . layout } ;
224+ }
225+
226+ const nextRowIndex = previous ?
227+ ( ctrl ? 0 : this . activeNode . row - 1 ) :
228+ ( ctrl ? this . grid . rowDimensionMrlRowsCollection . length - 1 : this . activeNode . row + 1 ) ;
229+ const nextRow = nextBlock || ctrl ? this . grid . rowDimensionMrlRowsCollection . toArray ( ) [ nextRowIndex ] : parentRow ;
230+ const nextRowStart = nextBlock ? ( previous ? nextRow . rowGroup . length : 1 ) : curCellLayout . rowStart + ( previous ? - 1 : 1 ) ;
231+ const maxColEnd = Math . max ( ...nextRow . contentCells . map ( cell => cell . layout . colEnd ) ) ;
232+ const nextColumnLayout = this . getNextVerticalColumnIndex (
233+ nextRow ,
234+ ctrl ? ( previous ? 1 : nextRow . rowGroup . length ) : nextRowStart ,
235+ homeEnd ? ( previous ? 1 : maxColEnd - 1 ) : this . activeNode . layout . colStart
236+ ) ;
237+ return {
238+ row : nextBlock || ctrl ? nextRowIndex : this . activeNode . row ,
239+ column : nextColumnLayout . columnVisibleIndex ,
240+ layout : {
241+ rowStart : nextColumnLayout . rowStart ,
242+ rowEnd : nextColumnLayout . rowEnd ,
243+ colStart : homeEnd ? nextColumnLayout . colStart : this . activeNode . layout . colStart ,
244+ colEnd : nextColumnLayout . colEnd ,
245+ columnVisibleIndex : nextColumnLayout . columnVisibleIndex
246+ } as IMultiRowLayoutNode
247+ } ;
248+ }
249+
250+ public getNextHorizontalPosition ( previous , ctrl ) {
251+ const parentRow = this . grid . rowDimensionMrlRowsCollection . toArray ( ) [ this . activeNode . row ] ;
252+ const maxColEnd = Math . max ( ...parentRow . contentCells . map ( cell => cell . layout . colEnd ) ) ;
253+ const curCellLayout = this . getNextVerticalColumnIndex ( parentRow , this . activeNode . layout . rowStart , this . activeNode . layout . colStart ) ;
254+
255+ if ( ( previous && curCellLayout . colStart === 1 ) || ( ! previous && curCellLayout . colEnd === maxColEnd ) ) {
256+ return { row : this . activeNode . row , column : this . activeNode . column , layout : this . activeNode . layout } ;
257+ }
258+
259+ const nextColumnLayout = this . getNextVerticalColumnIndex (
260+ parentRow ,
261+ this . activeNode . layout . rowStart ,
262+ ctrl ? ( previous ? 1 : maxColEnd - 1 ) : curCellLayout . colStart + ( previous ? - 1 : 1 )
263+ ) ;
264+ return {
265+ row : this . activeNode . row ,
266+ column : nextColumnLayout . columnVisibleIndex ,
267+ layout : {
268+ rowStart : this . activeNode . layout . rowStart ,
269+ rowEnd : nextColumnLayout . rowEnd ,
270+ colStart : nextColumnLayout . colStart ,
271+ colEnd : nextColumnLayout . colEnd ,
272+ columnVisibleIndex : nextColumnLayout . columnVisibleIndex
273+ } as IMultiRowLayoutNode
274+ } ;
275+ }
276+
277+
278+ private getNextVerticalColumnIndex ( nextRow : IgxPivotRowDimensionMrlRowComponent , newRowStart , newColStart ) {
279+ const nextCell = nextRow . contentCells . find ( cell => {
280+ return cell . layout . rowStart <= newRowStart && newRowStart < cell . layout . rowEnd &&
281+ cell . layout . colStart <= newColStart && newColStart < cell . layout . colEnd ;
282+ } ) ;
283+ return nextCell . layout ;
284+ }
77285}
0 commit comments