Skip to content

Commit 98dec29

Browse files
lorem--ipsumvogievetsky
authored andcommitted
Virtualized simple table
1 parent fc7bdfc commit 98dec29

File tree

4 files changed

+155
-42
lines changed

4 files changed

+155
-42
lines changed

src/client/components/simple-table/simple-table.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
}
5757

5858
.row {
59+
position: absolute;
60+
width: 100%;
5961
border-bottom: 1px solid $border-super-light;
6062
white-space: nowrap;
6163

@@ -69,6 +71,7 @@
6971

7072
.cell {
7173
@include ellipsis;
74+
position: absolute;
7275
padding: 14px 10px 5px 0;
7376

7477
&.has-icon {

src/client/components/simple-table/simple-table.tsx

Lines changed: 150 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ import { } from '../../../common/models/index';
2222

2323
import { 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

2729
export 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

6069
const 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
}

src/client/views/settings-view/data-table/data-table.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
}
2828

2929
.simple-table {
30+
height: calc(100% - 20px);
3031

3132
.header {
3233

src/server/utils/settings-manager/settings-manager.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,13 +411,12 @@ export class SettingsManager {
411411
start: day.move(maxTime, Timezone.UTC, -14),
412412
end: maxTime
413413
});
414-
return $('temp').filter(primaryTimeExpression.in(lastTwoWeeks)).limit(20).compute(context) as any;
414+
return $('temp').filter(primaryTimeExpression.in(lastTwoWeeks)).limit(500).compute(context) as any;
415415
});
416416
} else {
417417
return $('temp').limit(20).compute(context) as any;
418418
}
419419
});
420420
}
421421
}
422-
423422
}

0 commit comments

Comments
 (0)