@@ -22,7 +22,9 @@ import { } from '../../../common/models/index';
2222
2323import { classNames } from '../../utils/dom/dom' ;
2424
25- import { SvgIcon , Scroller , ScrollerPart } from '../index' ;
25+ import { SvgIcon } from '../svg-icon/svg-icon' ;
26+ import { Scroller , ScrollerPart } from '../scroller/scroller' ;
27+ import { GlobalEventListener } from '../global-event-listener/global-event-listener' ;
2628
2729export interface SimpleTableColumn {
2830 label : string ;
@@ -55,6 +57,13 @@ export interface SimpleTableState {
5557 hoveredRowIndex ?: number ;
5658 hoveredColumnIndex ?: number ;
5759 hoveredActionIndex ?: number ;
60+
61+ scrollTop ?: number ;
62+ scrollLeft ?: number ;
63+ viewportHeight ?: number ;
64+ viewportWidth ?: number ;
65+
66+ columnsPosition ?: number [ ] ;
5867}
5968
6069const ROW_HEIGHT = 42 ;
@@ -70,7 +79,58 @@ export class SimpleTable extends React.Component<SimpleTableProps, SimpleTableSt
7079 constructor ( ) {
7180 super ( ) ;
7281
73- this . state = { } ;
82+ this . state = {
83+ scrollLeft : 0 ,
84+ scrollTop : 0
85+ } ;
86+ }
87+
88+ componentWillReceiveProps ( nextProps : SimpleTableProps ) {
89+ this . computeColumnsPositions ( nextProps . columns ) ;
90+ }
91+
92+ componentDidMount ( ) {
93+ this . computeColumnsPositions ( this . props . columns ) ;
94+ this . onResize ( ) ;
95+ }
96+
97+ computeColumnsPositions ( columns : SimpleTableColumn [ ] ) {
98+ if ( columns ) {
99+ let columnsPosition : number [ ] = [ ] ;
100+
101+ let cumuledWidth = 0 ;
102+ columns . forEach ( ( c , i ) => {
103+ columnsPosition [ i ] = cumuledWidth ;
104+ cumuledWidth += c . width ;
105+ } ) ;
106+
107+ this . setState ( { columnsPosition} ) ;
108+ }
109+ }
110+
111+ onScroll ( scrollTop : number , scrollLeft : number ) {
112+ if ( this . state . scrollLeft !== scrollLeft || this . state . scrollTop !== scrollTop ) {
113+ this . setState ( {
114+ scrollLeft,
115+ scrollTop
116+ } ) ;
117+ }
118+ }
119+
120+ onResize ( ) {
121+ const { viewportWidth, viewportHeight } = this . state ;
122+
123+ var node = ReactDOM . findDOMNode ( this . refs [ 'scroller' ] ) ;
124+ if ( ! node ) return ;
125+
126+ var rect = node . getBoundingClientRect ( ) ;
127+
128+ if ( viewportHeight !== rect . height || viewportWidth !== rect . width ) {
129+ this . setState ( {
130+ viewportHeight : rect . height ,
131+ viewportWidth : rect . width
132+ } ) ;
133+ }
74134 }
75135
76136 renderHeaders ( columns : SimpleTableColumn [ ] , sortColumn : SimpleTableColumn , sortAscending : boolean ) : JSX . Element {
@@ -134,31 +194,6 @@ export class SimpleTable extends React.Component<SimpleTableProps, SimpleTableSt
134194 return column . field as ( row : any ) => any ;
135195 }
136196
137- renderRow ( row : any , columns : SimpleTableColumn [ ] , index : number ) : JSX . Element {
138- const { hoveredRowIndex } = this . state ;
139- var items : JSX . Element [ ] = [ ] ;
140-
141- for ( let i = 0 ; i < columns . length ; i ++ ) {
142- let col = columns [ i ] ;
143-
144- let icon = col . cellIcon ? < SvgIcon svg = { col . cellIcon } /> : null ;
145-
146- items . push ( < div
147- className = { classNames ( 'cell' , { 'has-icon' : ! ! col . cellIcon } ) }
148- style = { { width : col . width } }
149- key = { `cell-${ i } ` }
150- > { icon } { this . labelizer ( col ) ( row ) } </ div > ) ;
151- }
152-
153- return < div
154- className = { classNames ( 'row' , { hover : hoveredRowIndex === index } ) }
155- key = { `row-${ index } ` }
156- style = { { height : ROW_HEIGHT } }
157- >
158- { items }
159- </ div > ;
160- }
161-
162197 sortRows ( rows : any [ ] , sortColumn : SimpleTableColumn , sortAscending : boolean ) : any [ ] {
163198 if ( ! sortColumn ) return rows ;
164199
@@ -187,20 +222,90 @@ export class SimpleTable extends React.Component<SimpleTableProps, SimpleTableSt
187222 } ) ;
188223 }
189224
225+ renderRow ( row : any , columns : SimpleTableColumn [ ] , index : number , columnStart : number , columnEnd : number ) : JSX . Element {
226+ const { hoveredRowIndex, columnsPosition } = this . state ;
227+ var items : JSX . Element [ ] = [ ] ;
228+
229+ for ( let i = columnStart ; i < columnEnd ; i ++ ) {
230+ let col = columns [ i ] ;
231+
232+ let icon = col . cellIcon ? < SvgIcon svg = { col . cellIcon } /> : null ;
233+
234+ items . push ( < div
235+ className = { classNames ( 'cell' , { 'has-icon' : ! ! col . cellIcon } ) }
236+ style = { { width : col . width , left : columnsPosition [ i ] } }
237+ key = { `cell-${ i } ` }
238+ > { icon } { this . labelizer ( col ) ( row ) } </ div > ) ;
239+ }
240+
241+ return < div
242+ className = { classNames ( 'row' , { hover : hoveredRowIndex === index } ) }
243+ key = { `row-${ index } ` }
244+ style = { { height : ROW_HEIGHT , top : index * ROW_HEIGHT } }
245+ >
246+ { items }
247+ </ div > ;
248+ }
249+
190250 renderRows ( rows : any [ ] , columns : SimpleTableColumn [ ] , sortColumn : SimpleTableColumn , sortAscending : boolean ) : JSX . Element [ ] {
191- if ( ! rows || ! rows . length ) return null ;
251+ if ( ! rows || ! rows . length || ! columns || ! columns . length ) return null ;
252+
253+ const vertical = this . getYBoundaries ( rows ) ;
254+ const horizontal = this . getXBoundaries ( columns ) ;
192255
193256 rows = this . sortRows ( rows , sortColumn , sortAscending ) ;
194257
195258 var items : JSX . Element [ ] = [ ] ;
196259
197- for ( let i = 0 ; i < rows . length ; i ++ ) {
198- items . push ( this . renderRow ( rows [ i ] , columns , i ) ) ;
260+ for ( let i = vertical . start ; i < vertical . end ; i ++ ) {
261+ items . push ( this . renderRow ( rows [ i ] , columns , i , horizontal . start , horizontal . end ) ) ;
199262 }
200263
201264 return items ;
202265 }
203266
267+ getXBoundaries ( columns : SimpleTableColumn [ ] ) : { start : number , end : number } {
268+ const { viewportWidth, scrollLeft } = this . state ;
269+ var headerWidth = 0 ;
270+ var start = - 1 ;
271+ var end = - 1 ;
272+
273+ const n = columns . length ;
274+ for ( let i = 0 ; i < n ; i ++ ) {
275+ let col = columns [ i ] ;
276+
277+ if ( start === - 1 && headerWidth + col . width > scrollLeft ) {
278+ start = i ;
279+ }
280+
281+ if ( end === - 1 && headerWidth > scrollLeft + viewportWidth ) {
282+ end = i ;
283+ }
284+
285+ // To render last column
286+ if ( end === - 1 && i === n - 1 ) {
287+ end = n ;
288+ }
289+
290+ if ( start !== - 1 && end !== - 1 ) break ;
291+
292+ headerWidth += col . width ;
293+ }
294+
295+ return { start, end} ;
296+ }
297+
298+ getYBoundaries ( rows : any [ ] ) : { start : number , end : number } {
299+ const { viewportHeight, scrollTop } = this . state ;
300+
301+ var topIndex = Math . floor ( scrollTop / ROW_HEIGHT ) ;
302+
303+ return {
304+ start : topIndex ,
305+ end : Math . min ( topIndex + Math . ceil ( viewportHeight / ROW_HEIGHT ) , rows . length )
306+ } ;
307+ }
308+
204309 getLayout ( columns : SimpleTableColumn [ ] , rows : any [ ] , actions : SimpleTableAction [ ] ) {
205310 const width = columns . reduce ( ( a , b ) => a + b . width , 0 ) ;
206311
@@ -229,31 +334,34 @@ export class SimpleTable extends React.Component<SimpleTableProps, SimpleTableSt
229334 renderActions ( rows : any [ ] , actions : SimpleTableAction [ ] ) : JSX . Element [ ] {
230335 const { hoveredRowIndex, hoveredActionIndex } = this . state ;
231336
337+ const vertical = this . getYBoundaries ( rows ) ;
338+
232339 const directActions = this . getDirectActions ( actions ) ;
233340
234- const generator = ( row : any , i : number ) => {
341+ var elements : JSX . Element [ ] = [ ] ;
342+
343+ for ( let i = vertical . start ; i < vertical . end ; i ++ ) {
235344 let isRowHovered = i === hoveredRowIndex ;
345+ let row = rows [ i ] ;
236346
237347 let icons = directActions . map ( ( action , j ) => {
238348 return < div
239- className = { classNames ( " icon" , { hover : isRowHovered && j === hoveredActionIndex } ) }
349+ className = { classNames ( ' icon' , { hover : isRowHovered && j === hoveredActionIndex } ) }
240350 key = { `icon-${ j } ` }
241351 style = { { width : ACTION_WIDTH } }
242352 >
243353 < SvgIcon svg = { action . icon } />
244354 </ div > ;
245355 } ) ;
246356
247- return < div
248- className = { classNames ( " row action" , { hover : isRowHovered } ) }
357+ elements . push ( < div
358+ className = { classNames ( ' row action' , { hover : isRowHovered } ) }
249359 key = { `action-${ i } ` }
250- style = { { height : ROW_HEIGHT } }
251- >
252- { icons }
253- </ div > ;
254- } ;
360+ style = { { height : ROW_HEIGHT , top : i * ROW_HEIGHT } }
361+ > { icons } </ div > ) ;
362+ }
255363
256- return rows . map ( generator ) ;
364+ return elements ;
257365 }
258366
259367 getRowIndex ( y : number ) : number {
@@ -366,6 +474,7 @@ export class SimpleTable extends React.Component<SimpleTableProps, SimpleTableSt
366474 if ( ! columns ) return null ;
367475
368476 return < div className = { classNames ( "simple-table" , { clickable : hoveredRowIndex !== undefined } ) } >
477+ < GlobalEventListener resize = { this . onResize . bind ( this ) } />
369478 < Scroller
370479 ref = "scroller"
371480 layout = { this . getLayout ( columns , rows , actions ) }
@@ -379,6 +488,7 @@ export class SimpleTable extends React.Component<SimpleTableProps, SimpleTableSt
379488 onClick = { this . onClick . bind ( this ) }
380489 onMouseMove = { this . onMouseMove . bind ( this ) }
381490 onMouseLeave = { this . onMouseLeave . bind ( this ) }
491+ onScroll = { this . onScroll . bind ( this ) }
382492 />
383493 </ div > ;
384494 }
0 commit comments