Repository navigation
Expand file tree
/
Copy pathreact_smart_grid.js
More file actions
202 lines (181 loc) · 7.49 KB
/
Copy pathreact_smart_grid.js
File metadata and controls
202 lines (181 loc) · 7.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/*
Extrapolate columns from data
Colums are sortables (js or SQL)
Header stay visible (stick on screen limited by area)
Table is filtrable per column.
standard search widgets for columns header :
daterange
numericrange
multiselect sur categorical
text seach
Rows can use custom component
Cell can use custom component (blank cells are grayed by default)
Visibles Columns are selectables via dropdown select
Data is paginable for sql paginated data,
Record count is always visible
Pagination is shown
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd) { // AMD
define(['react'], factory);
} else if (typeof exports === 'object') { // Node, CommonJS-like
module.exports = factory(require('react'));
} else { // Browser globals (root is window)
root.ReactSmartTable = factory(root.React);
}
}(this, function (React) {
"use strict";
var exports = {};
function detectScrollbarWidthHeight() {
var div = document.createElement("div");
div.style.overflow = "scroll";
div.style.visibility = "hidden";
div.style.position = 'relative';
div.style.width = '100px';
div.style.height = '100px';
document.body.appendChild(div);
var d = {
width: div.offsetWidth - div.clientWidth,
height: div.offsetHeight - div.clientHeight
};
document.body.removeChild(div);
return d;
}
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val))
}
// render a row * col Grid with optional scrolling
// only render what is in viewport
var G = React.createClass({displayName: "Grid",
getDefaultProps: function() {
return {
width:200, // fixed width
height:200, // fixed height
x: 0,
y: 0,
overflowX:"hidden", // 'hidden', 'auto', 'overflow'
overflowY:"hidden", // 'hidden', 'auto', 'overflow'
// return the number of rows
rowsCountGetter: function(){ return 0; },
// return the data associated with the row[index]
rowDataGetter: function(index) { return {}; },
// return the height of the row[index]
rowHeightGetter: function(index) { return 30; },
defaultColumn:{
width:60,
dataKey:"id",
cellDataGetter:function(rowData, rowIndex, column, columnIndex) {
return rowData[column.dataKey];
},
cellRenderer:function(cellData, rowData, rowIndex, column, columnIndex) {
return cellData;
}
},
columns:[],
onScroll:null,
};
},
getInitialState: function() {
return {
scrollTop:0,
scrollLeft:0
};
},
componentWillMount: function() {
this.SB = detectScrollbarWidthHeight();
this.computeExtents();
},
computeExtents: function() {
var colsExtents = [0];
var val = 0;
for(var i=0, len = this.props.columns.length ; i<len; i++) {
var column = this.props.columns[i];
val += ("width" in column) ? column.width : this.props.defaultColumn.width;
colsExtents.push(val);
}
this.colsExtents = colsExtents;
var rowsExtents = [0];
val = 0;
for(var i=0, len = this.props.rowsCountGetter(); i<len; i++) {
val+=this.props.rowHeightGetter(i);
rowsExtents.push(val);
}
this.rowsExtents = rowsExtents;
},
getVisibleSlice: function (extents, minVal, maxVal)
{
var i=1, len = extents.length;
if(len<2)
return {begin:0, end:0};
while( i<len && extents[i] < minVal) { i++;}
var begin = i-1;
len--;
while( i<len && extents[i] < maxVal) { i++; }
var end = i;// Math.min(i,len-1);
return {begin:begin, end:end};
},
handleScroll: function(e) {
this.setState({
scrollTop :e.target.scrollTop,
scrollLeft:e.target.scrollLeft
});
if(this.props.onScroll)
this.props.onScroll(e);
},
render: function() {
// viewport size
var width = this.props.width;
var height = this.props.height;
// inner frame size
var innerWidth = this.colsExtents[this.colsExtents.length-1];
var innerHeight = this.rowsExtents[this.rowsExtents.length-1];
// inner frame decal
var scrollLeft = this.state.scrollLeft;
var scrollTop = this.state.scrollTop;
var rowSlice = this.getVisibleSlice(this.rowsExtents, scrollTop, scrollTop + height);
var colSlice = this.getVisibleSlice(this.colsExtents, scrollLeft, scrollLeft + width);
var cells = [];
// compute central cells
for(var rowIndex = rowSlice.begin; rowIndex < rowSlice.end; rowIndex++) {
var rowData = this.props.rowDataGetter(rowIndex);
var reactKeyBase = (rowIndex-rowSlice.begin) * this.props.columns.length;
for(var columnIndex = colSlice.begin; columnIndex < colSlice.end; columnIndex++) {
var column = this.props.columns[columnIndex];
var cellData = ('cellDataGetter' in column)?
column.cellDataGetter(rowData, rowIndex, column, columnIndex):
this.props.defaultColumn.cellDataGetter(rowData, rowIndex, column, columnIndex);
var cellElem = ('cellRenderer' in column)?
column.cellRenderer(cellData, rowData, rowIndex, column, columnIndex):
this.props.defaultColumn.cellRenderer(cellData, rowData, rowIndex, column, columnIndex);
var style = {
//position:"absolute",
//overflowX: 'hidden',
//overflowY: 'hidden',
left: this.colsExtents[columnIndex],
top: this.rowsExtents[rowIndex],
width: ("width" in column) ? column.width : this.props.defaultColumn.width,
height: this.props.rowHeightGetter(rowIndex)
};
cells.push( React.DOM.div( {style:style, key:reactKeyBase+columnIndex, className:"rst_cell"}, cellElem) );
}
}
return React.DOM.div(
{ scrollTop:scrollTop,
scrollLeft:scrollLeft,
style:{
position:"absolute",
left:this.props.x,
top:this.props.y,
width:width,
height:height,
overflowX:this.props.overflowX,
overflowY:this.props.overflowY
},
onScroll:this.handleScroll
}, React.DOM.div( {style:{ position:"relative", width:innerWidth, height:innerHeight}}, cells)
);
}
});
var Grid = exports.Grid = React.createFactory(G);
return exports;
}));