├── LICENSE ├── dist ├── css │ ├── style.css │ └── style.min.css ├── index.html └── js │ └── table.js ├── gulpfile.js ├── package.json └── src ├── css ├── _fixed-data-table.scss ├── _reset.scss └── style.scss ├── index.html └── js ├── FittedTable.jsx ├── LoadingCellContainer.jsx ├── LoadingObjectContainer.jsx ├── ObjectData.jsx ├── ObjectDataListStore.jsx ├── SetContainerSize.jsx ├── TouchScrollArea.jsx ├── TouchableArea.jsx ├── ZyngaScroller.jsx ├── detect-element-resize.js └── main.jsx /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, ofersadgat 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of fixed-data-table-simple nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /dist/css/style.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { 6 | margin: 0; 7 | padding: 0; 8 | border: 0; 9 | font-size: 100%; 10 | font: inherit; 11 | vertical-align: baseline; } 12 | 13 | /* HTML5 display-role reset for older browsers */ 14 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { 15 | display: block; } 16 | 17 | body { 18 | line-height: 1; } 19 | 20 | ol, ul { 21 | list-style: none; } 22 | 23 | blockquote, q { 24 | quotes: none; } 25 | 26 | blockquote:before, blockquote:after, q:before, q:after { 27 | content: ''; 28 | content: none; } 29 | 30 | table { 31 | border-collapse: collapse; 32 | border-spacing: 0; } 33 | 34 | /** 35 | * FixedDataTable v0.1.0 36 | * 37 | * Copyright (c) 2015, Facebook, Inc. 38 | * All rights reserved. 39 | * 40 | * This source code is licensed under the BSD-style license found in the 41 | * LICENSE file in the root directory of this source tree. An additional grant 42 | * of patent rights can be found in the PATENTS file in the same directory. 43 | */ 44 | /** 45 | * Copyright (c) 2015, Facebook, Inc. 46 | * All rights reserved. 47 | * 48 | * This source code is licensed under the BSD-style license found in the 49 | * LICENSE file in the root directory of this source tree. An additional grant 50 | * of patent rights can be found in the PATENTS file in the same directory. 51 | * 52 | * @providesModule Scrollbar 53 | */ 54 | .public_Scrollbar_main { 55 | box-sizing: border-box; 56 | outline: none; 57 | overflow: hidden; 58 | position: absolute; 59 | -webkit-transition-duration: 250ms; 60 | transition-duration: 250ms; 61 | -webkit-transition-timing-function: ease; 62 | transition-timing-function: ease; 63 | -webkit-user-select: none; 64 | -moz-user-select: none; 65 | -ms-user-select: none; 66 | user-select: none; } 67 | 68 | .public_Scrollbar_mainVertical { 69 | bottom: 0; 70 | right: 0; 71 | top: 0; 72 | -webkit-transition-property: background-color width; 73 | transition-property: background-color width; 74 | width: 15px; } 75 | 76 | /* Touching the scroll-track directly makes the scroll-track bolder */ 77 | .public_Scrollbar_mainVertical.Scrollbar_mainActive, .public_Scrollbar_mainVertical:hover { 78 | background-color: rgba(255, 255, 255, 0.8); 79 | width: 17px; } 80 | 81 | .public_Scrollbar_mainHorizontal { 82 | bottom: 0; 83 | height: 15px; 84 | left: 0; 85 | -webkit-transition-property: background-color height; 86 | transition-property: background-color height; } 87 | 88 | /* Touching the scroll-track directly makes the scroll-track bolder */ 89 | .public_Scrollbar_mainHorizontal.Scrollbar_mainActive, .public_Scrollbar_mainHorizontal:hover { 90 | background-color: rgba(255, 255, 255, 0.8); 91 | height: 17px; } 92 | 93 | .Scrollbar_mainOpaque, .Scrollbar_mainOpaque.Scrollbar_mainActive, .Scrollbar_mainOpaque:hover { 94 | background-color: #fff; } 95 | 96 | .Scrollbar_face { 97 | left: 0; 98 | overflow: hidden; 99 | position: absolute; 100 | z-index: 1; } 101 | 102 | /** 103 | * This selector renders the "nub" of the scrollface. The nub must 104 | * be rendered as pseudo-element so that it won't receive any UI events then 105 | * we can get the correct `event.offsetX` and `event.offsetY` from the 106 | * scrollface element while dragging it. 107 | */ 108 | .Scrollbar_face:after { 109 | background-color: #c2c2c2; 110 | border-radius: 6px; 111 | content: ''; 112 | display: block; 113 | position: absolute; 114 | -webkit-transition: background-color 250ms ease; 115 | transition: background-color 250ms ease; } 116 | 117 | .public_Scrollbar_main:hover .Scrollbar_face:after, .Scrollbar_mainActive .Scrollbar_face:after, .Scrollbar_faceActive:after { 118 | background-color: #7d7d7d; } 119 | 120 | .Scrollbar_faceHorizontal { 121 | bottom: 0; 122 | left: 0; 123 | top: 0; } 124 | 125 | .Scrollbar_faceHorizontal:after { 126 | bottom: 4px; 127 | left: 0; 128 | top: 4px; 129 | width: 100%; } 130 | 131 | .Scrollbar_faceVertical { 132 | left: 0; 133 | right: 0; 134 | top: 0; } 135 | 136 | .Scrollbar_faceVertical:after { 137 | height: 100%; 138 | left: 4px; 139 | right: 4px; 140 | top: 0; } 141 | 142 | /** 143 | * Copyright (c) 2015, Facebook, Inc. 144 | * All rights reserved. 145 | * 146 | * This source code is licensed under the BSD-style license found in the 147 | * LICENSE file in the root directory of this source tree. An additional grant 148 | * of patent rights can be found in the PATENTS file in the same directory. 149 | * 150 | * @providesModule fixedDataTable 151 | */ 152 | .public_fixedDataTable_main { 153 | border: solid 1px #d3d3d3; 154 | overflow: hidden; 155 | position: relative; } 156 | 157 | .public_fixedDataTable_header, .fixedDataTable_hasBottomBorder { 158 | border-bottom: solid 1px #d3d3d3; } 159 | 160 | .public_fixedDataTable_header .public_fixedDataTableCell_main { 161 | font-weight: bold; } 162 | 163 | .public_fixedDataTable_header, .public_fixedDataTable_header .public_fixedDataTableCell_main { 164 | background-color: #f6f7f8; 165 | background-image: -webkit-linear-gradient(#fff, #efefef); 166 | background-image: linear-gradient(#fff, #efefef); } 167 | 168 | .public_fixedDataTable_footer .public_fixedDataTableCell_main { 169 | background-color: #f6f7f8; 170 | border-top: solid 1px #d3d3d3; } 171 | 172 | .fixedDataTable_shadow { 173 | background: 0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAECAYAAABP2FU6AAAAF0lEQVR4AWPUkNeSBhHCjJoK2twgFisAFagCCp3pJlAAAAAASUVORK5CYII=) repeat-x; 174 | height: 4px; 175 | left: 0; 176 | position: absolute; 177 | right: 0; 178 | z-index: 1; } 179 | 180 | .fixedDataTable_rowsContainer { 181 | overflow: hidden; 182 | position: relative; } 183 | 184 | .fixedDataTable_horizontalScrollbar { 185 | bottom: 0; 186 | position: absolute; } 187 | 188 | .fixedDataTable_horizontalScrollbar .public_Scrollbar_mainHorizontal { 189 | background: #fff; } 190 | 191 | /** 192 | * Copyright (c) 2015, Facebook, Inc. 193 | * All rights reserved. 194 | * 195 | * This source code is licensed under the BSD-style license found in the 196 | * LICENSE file in the root directory of this source tree. An additional grant 197 | * of patent rights can be found in the PATENTS file in the same directory. 198 | * 199 | * @providesModule fixedDataTableCell 200 | */ 201 | .public_fixedDataTableCell_main { 202 | background-color: #fff; 203 | border: solid 1px #d3d3d3; 204 | border-width: 0 1px 0 0; 205 | box-sizing: border-box; 206 | display: block; 207 | overflow: hidden; 208 | position: relative; 209 | white-space: normal; } 210 | 211 | .public_fixedDataTableCell_lastChild { 212 | border-width: 0 1px 1px 0; } 213 | 214 | .public_fixedDataTableCell_highlighted { 215 | background-color: #f4f4f4; } 216 | 217 | .public_fixedDataTableCell_alignRight { 218 | text-align: right; } 219 | 220 | .public_fixedDataTableCell_alignCenter { 221 | text-align: center; } 222 | 223 | .public_fixedDataTableCell_wrap1 { 224 | display: table; 225 | overflow: hidden; } 226 | 227 | .public_fixedDataTableCell_wrap2 { 228 | display: table-row; } 229 | 230 | .public_fixedDataTableCell_wrap3 { 231 | display: table-cell; 232 | vertical-align: middle; } 233 | 234 | .public_fixedDataTableCell_cellContent { 235 | padding: 8px; } 236 | 237 | .fixedDataTableCell_columnResizerContainer { 238 | position: absolute; 239 | right: 0px; 240 | width: 6px; 241 | z-index: 1; } 242 | 243 | .fixedDataTableCell_columnResizerContainer:hover { 244 | cursor: ew-resize; } 245 | 246 | .fixedDataTableCell_columnResizerContainer:hover .fixedDataTableCell_columnResizerKnob { 247 | visibility: visible; } 248 | 249 | .fixedDataTableCell_columnResizerKnob { 250 | background-color: #0284ff; 251 | position: absolute; 252 | right: 0px; 253 | visibility: hidden; 254 | width: 4px; } 255 | 256 | /** 257 | * Copyright (c) 2015, Facebook, Inc. 258 | * All rights reserved. 259 | * 260 | * This source code is licensed under the BSD-style license found in the 261 | * LICENSE file in the root directory of this source tree. An additional grant 262 | * of patent rights can be found in the PATENTS file in the same directory. 263 | * 264 | * @providesModule fixedDataTableCellGroup 265 | */ 266 | .fixedDataTableCellGroup_cellGroup { 267 | -webkit-backface-visibility: hidden; 268 | backface-visibility: hidden; 269 | left: 0; 270 | overflow: hidden; 271 | position: absolute; 272 | top: 0; 273 | white-space: nowrap; } 274 | 275 | .fixedDataTableCellGroup_cellGroup > .public_fixedDataTableCell_main { 276 | display: inline-block; 277 | vertical-align: top; 278 | white-space: normal; } 279 | 280 | .fixedDataTableCellGroup_cellGroupWrapper { 281 | position: absolute; 282 | top: 0; } 283 | 284 | /** 285 | * Copyright (c) 2015, Facebook, Inc. 286 | * All rights reserved. 287 | * 288 | * This source code is licensed under the BSD-style license found in the 289 | * LICENSE file in the root directory of this source tree. An additional grant 290 | * of patent rights can be found in the PATENTS file in the same directory. 291 | * 292 | * @providesModule fixedDataTableColumnResizerLine 293 | */ 294 | .fixedDataTableColumnResizerLine_mouseArea { 295 | cursor: ew-resize; 296 | position: absolute; 297 | right: -5px; 298 | width: 12px; } 299 | 300 | .fixedDataTableColumnResizerLine_main { 301 | border-right: 1px solid #0284ff; 302 | box-sizing: border-box; 303 | position: absolute; 304 | z-index: 10; } 305 | 306 | .fixedDataTableColumnResizerLine_hiddenElem { 307 | display: none !important; } 308 | 309 | /** 310 | * Copyright (c) 2015, Facebook, Inc. 311 | * All rights reserved. 312 | * 313 | * This source code is licensed under the BSD-style license found in the 314 | * LICENSE file in the root directory of this source tree. An additional grant 315 | * of patent rights can be found in the PATENTS file in the same directory. 316 | * 317 | * @providesModule fixedDataTableRow 318 | */ 319 | .public_fixedDataTableRow_main { 320 | background-color: #fff; 321 | box-sizing: border-box; 322 | overflow: hidden; 323 | position: absolute; 324 | top: 0; } 325 | 326 | .fixedDataTableRow_body { 327 | left: 0; 328 | position: absolute; 329 | top: 0; } 330 | 331 | .public_fixedDataTableRow_highlighted, .public_fixedDataTableRow_highlighted .public_fixedDataTableCell_main { 332 | background-color: #f6f7f8; } 333 | 334 | .fixedDataTableRow_fixedColumnsDivider { 335 | -webkit-backface-visibility: hidden; 336 | backface-visibility: hidden; 337 | border-left: solid 1px #d3d3d3; 338 | left: 0; 339 | position: absolute; 340 | top: 0; 341 | width: 0; } 342 | 343 | .fixedDataTableRow_columnsShadow { 344 | background: 0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAYAAAD5PA/NAAAAFklEQVQIHWPSkNeSBmJhTQVtbiDNCgASagIIuJX8OgAAAABJRU5ErkJggg==) repeat-y; 345 | width: 4px; } 346 | 347 | .fixedDataTableRow_rowWrapper { 348 | position: absolute; 349 | top: 0; } 350 | 351 | body, #client { 352 | max-height: 100%; 353 | height: 100%; 354 | max-width: 100%; 355 | width: 100%; 356 | position: fixed; } 357 | -------------------------------------------------------------------------------- /dist/css/style.min.css: -------------------------------------------------------------------------------- 1 | a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:'';content:none}table{border-collapse:collapse;border-spacing:0}.public_Scrollbar_main{box-sizing:border-box;outline:0;overflow:hidden;position:absolute;-webkit-transition-duration:250ms;transition-duration:250ms;-webkit-transition-timing-function:ease;transition-timing-function:ease;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.public_Scrollbar_mainVertical{bottom:0;right:0;top:0;-webkit-transition-property:background-color width;transition-property:background-color width;width:15px}.public_Scrollbar_mainVertical.Scrollbar_mainActive,.public_Scrollbar_mainVertical:hover{background-color:rgba(255,255,255,.8);width:17px}.public_Scrollbar_mainHorizontal{bottom:0;height:15px;left:0;-webkit-transition-property:background-color height;transition-property:background-color height}.public_Scrollbar_mainHorizontal.Scrollbar_mainActive,.public_Scrollbar_mainHorizontal:hover{background-color:rgba(255,255,255,.8);height:17px}.Scrollbar_mainOpaque,.Scrollbar_mainOpaque.Scrollbar_mainActive,.Scrollbar_mainOpaque:hover{background-color:#fff}.Scrollbar_face{left:0;overflow:hidden;position:absolute;z-index:1}.Scrollbar_face:after{background-color:#c2c2c2;border-radius:6px;content:'';display:block;position:absolute;-webkit-transition:background-color 250ms ease;transition:background-color 250ms ease}.Scrollbar_faceActive:after,.Scrollbar_mainActive .Scrollbar_face:after,.public_Scrollbar_main:hover .Scrollbar_face:after{background-color:#7d7d7d}.Scrollbar_faceHorizontal{bottom:0;left:0;top:0}.Scrollbar_faceHorizontal:after{bottom:4px;left:0;top:4px;width:100%}.Scrollbar_faceVertical{left:0;right:0;top:0}.Scrollbar_faceVertical:after{height:100%;left:4px;right:4px;top:0}.public_fixedDataTable_main{border:1px solid #d3d3d3;overflow:hidden;position:relative}.fixedDataTable_hasBottomBorder,.public_fixedDataTable_header{border-bottom:solid 1px #d3d3d3}.public_fixedDataTable_header .public_fixedDataTableCell_main{font-weight:700}.public_fixedDataTable_header,.public_fixedDataTable_header .public_fixedDataTableCell_main{background-color:#f6f7f8;background-image:-webkit-linear-gradient(#fff,#efefef);background-image:linear-gradient(#fff,#efefef)}.public_fixedDataTable_footer .public_fixedDataTableCell_main{background-color:#f6f7f8;border-top:solid 1px #d3d3d3}.fixedDataTable_shadow{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAECAYAAABP2FU6AAAAF0lEQVR4AWPUkNeSBhHCjJoK2twgFisAFagCCp3pJlAAAAAASUVORK5CYII=) repeat-x;height:4px;left:0;position:absolute;right:0;z-index:1}.fixedDataTable_rowsContainer{overflow:hidden;position:relative}.fixedDataTable_horizontalScrollbar{bottom:0;position:absolute}.fixedDataTable_horizontalScrollbar .public_Scrollbar_mainHorizontal{background:#fff}.public_fixedDataTableCell_main{background-color:#fff;border:1px solid #d3d3d3;border-width:0 1px 0 0;box-sizing:border-box;display:block;overflow:hidden;position:relative;white-space:normal}.public_fixedDataTableCell_lastChild{border-width:0 1px 1px 0}.public_fixedDataTableCell_highlighted{background-color:#f4f4f4}.public_fixedDataTableCell_alignRight{text-align:right}.public_fixedDataTableCell_alignCenter{text-align:center}.public_fixedDataTableCell_wrap1{display:table;overflow:hidden}.public_fixedDataTableCell_wrap2{display:table-row}.public_fixedDataTableCell_wrap3{display:table-cell;vertical-align:middle}.public_fixedDataTableCell_cellContent{padding:8px}.fixedDataTableCell_columnResizerContainer{position:absolute;right:0;width:6px;z-index:1}.fixedDataTableCell_columnResizerContainer:hover{cursor:ew-resize}.fixedDataTableCell_columnResizerContainer:hover .fixedDataTableCell_columnResizerKnob{visibility:visible}.fixedDataTableCell_columnResizerKnob{background-color:#0284ff;position:absolute;right:0;visibility:hidden;width:4px}.fixedDataTableCellGroup_cellGroup{-webkit-backface-visibility:hidden;backface-visibility:hidden;left:0;overflow:hidden;position:absolute;top:0;white-space:nowrap}.fixedDataTableCellGroup_cellGroup>.public_fixedDataTableCell_main{display:inline-block;vertical-align:top;white-space:normal}.fixedDataTableCellGroup_cellGroupWrapper{position:absolute;top:0}.fixedDataTableColumnResizerLine_mouseArea{cursor:ew-resize;position:absolute;right:-5px;width:12px}.fixedDataTableColumnResizerLine_main{border-right:1px solid #0284ff;box-sizing:border-box;position:absolute;z-index:10}.fixedDataTableColumnResizerLine_hiddenElem{display:none!important}.public_fixedDataTableRow_main{background-color:#fff;box-sizing:border-box;overflow:hidden;position:absolute;top:0}.fixedDataTableRow_body{left:0;position:absolute;top:0}.public_fixedDataTableRow_highlighted,.public_fixedDataTableRow_highlighted .public_fixedDataTableCell_main{background-color:#f6f7f8}.fixedDataTableRow_fixedColumnsDivider{-webkit-backface-visibility:hidden;backface-visibility:hidden;border-left:solid 1px #d3d3d3;left:0;position:absolute;top:0;width:0}.fixedDataTableRow_columnsShadow{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAYAAAD5PA/NAAAAFklEQVQIHWPSkNeSBmJhTQVtbiDNCgASagIIuJX8OgAAAABJRU5ErkJggg==) repeat-y;width:4px}.fixedDataTableRow_rowWrapper{position:absolute;top:0}#client,body{max-height:100%;height:100%;max-width:100%;width:100%;position:fixed} -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var browserify = require('gulp-browserify'); 3 | var concat = require('gulp-concat'); 4 | var sass = require('gulp-sass'); 5 | var autoprefixer = require('gulp-autoprefixer'); 6 | var minifycss = require('gulp-minify-css'); 7 | var jshint = require('gulp-jshint'); 8 | var uglify = require('gulp-uglify'); 9 | var imagemin = require('gulp-imagemin'); 10 | var rename = require('gulp-rename'); 11 | var concat = require('gulp-concat'); 12 | var notify = require('gulp-notify'); 13 | var cache = require('gulp-cache'); 14 | var plumber = require('gulp-plumber'); 15 | var browserSync = require('browser-sync'); 16 | var del = require('del'); 17 | 18 | // Static server 19 | gulp.task('browser-sync', function() { 20 | browserSync({ 21 | notify: false, 22 | server: { 23 | baseDir: "./dist" 24 | } 25 | }); 26 | }); 27 | 28 | gulp.task('browserify', function(){ 29 | return gulp.src('src/js/main.jsx') 30 | .pipe(plumber()) 31 | .pipe(browserify({transform: 'reactify'})) 32 | .pipe(concat('table.js')) 33 | .pipe(plumber.stop()) 34 | .pipe(gulp.dest('dist/js')); 35 | }) 36 | 37 | gulp.task('js', function() { 38 | return gulp.src('src/js/**/*.js') 39 | .pipe(jshint('.jshintrc')) 40 | .pipe(jshint.reporter('default')) 41 | .pipe(concat('main.js')) 42 | .pipe(gulp.dest('dist/assets/js')) 43 | .pipe(rename({suffix: '.min'})) 44 | .pipe(uglify()) 45 | .pipe(gulp.dest('dist/assets/js')) 46 | .pipe(notify({ message: 'Scripts task complete' })); 47 | }); 48 | 49 | gulp.task('css', function() { 50 | return gulp.src('src/css/*') 51 | .pipe(plumber()) 52 | .pipe(sass({ errLogToConsole: true })) 53 | .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1')) 54 | .pipe(concat('style.css')) 55 | .pipe(gulp.dest('dist/css')) 56 | .pipe(rename({suffix: '.min'})) 57 | .pipe(minifycss()) 58 | .pipe(plumber.stop()) 59 | .pipe(gulp.dest('dist/css')); 60 | }); 61 | 62 | gulp.task('clean', function(cb) { 63 | del(['dist'], cb) 64 | }); 65 | 66 | gulp.task('html', function(){ 67 | return gulp.src('src/index.html') 68 | .pipe(gulp.dest('dist')); 69 | }); 70 | 71 | gulp.task('images', function(){ 72 | return gulp.src('src/images/*') 73 | .pipe(gulp.dest('dist/images')); 74 | }); 75 | 76 | gulp.task('fonts', function(){ 77 | return gulp.src('src/fonts/*') 78 | .pipe(gulp.dest('dist/fonts')); 79 | }); 80 | 81 | gulp.task('default', ['browserify', 'css', 'images', 'fonts', 'html']); 82 | 83 | gulp.task('watch', ['browser-sync'], function () { 84 | 85 | gulp.watch('src/**/*.*', ['default', browserSync.reload]); 86 | }); 87 | 88 | 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixed-data-table-simple", 3 | "version": "1.0.0", 4 | "description": "This is a lot of the boilerplate code necessary to use fixed-data-table", 5 | "main": "src/js/main.jsx", 6 | "dependencies": { 7 | "browser-sync": "^2.0.1", 8 | "del": "^1.1.1", 9 | "gulp": "^3.8.11", 10 | "gulp-autoprefixer": "^2.1.0", 11 | "fixed-data-table": "^0.1.0", 12 | "gulp-browserify": "^0.5.1", 13 | "gulp-cache": "^0.2.4", 14 | "gulp-concat": "^2.4.3", 15 | "gulp-imagemin": "^2.2.0", 16 | "gulp-jshint": "^1.9.2", 17 | "gulp-minify-css": "^0.4.5", 18 | "gulp-plumber": "^0.6.6", 19 | "gulp-notify": "^2.2.0", 20 | "gulp-rename": "^1.2.0", 21 | "gulp-sass": "^1.3.2", 22 | "gulp-uglify": "^1.1.0", 23 | "jquery": "^2.1.3", 24 | "object-assign": "^2.0.0", 25 | "react": "^0.12.2", 26 | "reactify": "^1.0.0", 27 | "events": "^1.0.2" 28 | }, 29 | "devDependencies": {}, 30 | "scripts": { 31 | "test": "echo \"Error: no test specified\" && exit 1" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/ofersadgat/fixed-data-table-simple.git" 36 | }, 37 | "author": "Ofer Sadgat", 38 | "license": "BSD", 39 | "bugs": { 40 | "url": "https://github.com/ofersadgat/fixed-data-table-simple/issues" 41 | }, 42 | "homepage": "https://github.com/ofersadgat/fixed-data-table-simple" 43 | } 44 | -------------------------------------------------------------------------------- /src/css/_fixed-data-table.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * FixedDataTable v0.1.0 3 | * 4 | * Copyright (c) 2015, Facebook, Inc. 5 | * All rights reserved. 6 | * 7 | * This source code is licensed under the BSD-style license found in the 8 | * LICENSE file in the root directory of this source tree. An additional grant 9 | * of patent rights can be found in the PATENTS file in the same directory. 10 | */ 11 | 12 | /** 13 | * Copyright (c) 2015, Facebook, Inc. 14 | * All rights reserved. 15 | * 16 | * This source code is licensed under the BSD-style license found in the 17 | * LICENSE file in the root directory of this source tree. An additional grant 18 | * of patent rights can be found in the PATENTS file in the same directory. 19 | * 20 | * @providesModule Scrollbar 21 | */ 22 | 23 | .public_Scrollbar_main { 24 | box-sizing: border-box; 25 | outline: none; 26 | overflow: hidden; 27 | position: absolute; 28 | -webkit-transition-duration: 250ms; 29 | transition-duration: 250ms; 30 | -webkit-transition-timing-function: ease; 31 | transition-timing-function: ease; 32 | -webkit-user-select: none; 33 | -moz-user-select: none; 34 | -ms-user-select: none; 35 | user-select: none; 36 | } 37 | 38 | .public_Scrollbar_mainVertical { 39 | bottom: 0; 40 | right: 0; 41 | top: 0; 42 | -webkit-transition-property: background-color width; 43 | transition-property: background-color width; 44 | width: 15px; 45 | } 46 | 47 | /* Touching the scroll-track directly makes the scroll-track bolder */ 48 | .public_Scrollbar_mainVertical.Scrollbar_mainActive, 49 | .public_Scrollbar_mainVertical:hover { 50 | background-color: rgba(255, 255, 255, 0.8); 51 | width: 17px; 52 | } 53 | 54 | .public_Scrollbar_mainHorizontal { 55 | bottom: 0; 56 | height: 15px; 57 | left: 0; 58 | -webkit-transition-property: background-color height; 59 | transition-property: background-color height; 60 | } 61 | 62 | /* Touching the scroll-track directly makes the scroll-track bolder */ 63 | .public_Scrollbar_mainHorizontal.Scrollbar_mainActive, 64 | .public_Scrollbar_mainHorizontal:hover { 65 | background-color: rgba(255, 255, 255, 0.8); 66 | height: 17px; 67 | } 68 | 69 | .Scrollbar_mainOpaque, 70 | .Scrollbar_mainOpaque.Scrollbar_mainActive, 71 | .Scrollbar_mainOpaque:hover { 72 | background-color: #fff; 73 | } 74 | 75 | .Scrollbar_face { 76 | left: 0; 77 | overflow: hidden; 78 | position: absolute; 79 | z-index: 1; 80 | } 81 | 82 | /** 83 | * This selector renders the "nub" of the scrollface. The nub must 84 | * be rendered as pseudo-element so that it won't receive any UI events then 85 | * we can get the correct `event.offsetX` and `event.offsetY` from the 86 | * scrollface element while dragging it. 87 | */ 88 | .Scrollbar_face:after { 89 | background-color: #c2c2c2; 90 | border-radius: 6px; 91 | content: ''; 92 | display: block; 93 | position: absolute; 94 | -webkit-transition: background-color 250ms ease; 95 | transition: background-color 250ms ease; 96 | } 97 | 98 | .public_Scrollbar_main:hover .Scrollbar_face:after, 99 | .Scrollbar_mainActive .Scrollbar_face:after, 100 | .Scrollbar_faceActive:after { 101 | background-color: #7d7d7d; 102 | } 103 | 104 | .Scrollbar_faceHorizontal { 105 | bottom: 0; 106 | left: 0; 107 | top: 0; 108 | } 109 | 110 | .Scrollbar_faceHorizontal:after { 111 | bottom: 4px; 112 | left: 0; 113 | top: 4px; 114 | width: 100%; 115 | } 116 | 117 | .Scrollbar_faceVertical { 118 | left: 0; 119 | right: 0; 120 | top: 0; 121 | } 122 | 123 | .Scrollbar_faceVertical:after { 124 | height: 100%; 125 | left: 4px; 126 | right: 4px; 127 | top: 0; 128 | } 129 | /** 130 | * Copyright (c) 2015, Facebook, Inc. 131 | * All rights reserved. 132 | * 133 | * This source code is licensed under the BSD-style license found in the 134 | * LICENSE file in the root directory of this source tree. An additional grant 135 | * of patent rights can be found in the PATENTS file in the same directory. 136 | * 137 | * @providesModule fixedDataTable 138 | */ 139 | 140 | .public_fixedDataTable_main { 141 | border: solid 1px #d3d3d3; 142 | overflow: hidden; 143 | position: relative; 144 | } 145 | 146 | .public_fixedDataTable_header, 147 | .fixedDataTable_hasBottomBorder { 148 | border-bottom: solid 1px #d3d3d3; 149 | } 150 | 151 | .public_fixedDataTable_header .public_fixedDataTableCell_main { 152 | font-weight: bold; 153 | } 154 | 155 | .public_fixedDataTable_header, 156 | .public_fixedDataTable_header .public_fixedDataTableCell_main { 157 | background-color: #f6f7f8; 158 | background-image: -webkit-linear-gradient(#fff, #efefef); 159 | background-image: linear-gradient(#fff, #efefef); 160 | } 161 | 162 | .public_fixedDataTable_footer .public_fixedDataTableCell_main { 163 | background-color: #f6f7f8; 164 | border-top: solid 1px #d3d3d3; 165 | } 166 | 167 | .fixedDataTable_shadow { 168 | background: 0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAECAYAAABP2FU6AAAAF0lEQVR4AWPUkNeSBhHCjJoK2twgFisAFagCCp3pJlAAAAAASUVORK5CYII=) repeat-x; 169 | height: 4px; 170 | left: 0; 171 | position: absolute; 172 | right: 0; 173 | z-index: 1; 174 | } 175 | 176 | .fixedDataTable_rowsContainer { 177 | overflow: hidden; 178 | position: relative; 179 | } 180 | 181 | .fixedDataTable_horizontalScrollbar { 182 | bottom: 0; 183 | position: absolute; 184 | } 185 | 186 | .fixedDataTable_horizontalScrollbar .public_Scrollbar_mainHorizontal { 187 | background: #fff; 188 | } 189 | /** 190 | * Copyright (c) 2015, Facebook, Inc. 191 | * All rights reserved. 192 | * 193 | * This source code is licensed under the BSD-style license found in the 194 | * LICENSE file in the root directory of this source tree. An additional grant 195 | * of patent rights can be found in the PATENTS file in the same directory. 196 | * 197 | * @providesModule fixedDataTableCell 198 | */ 199 | 200 | .public_fixedDataTableCell_main { 201 | background-color: #fff; 202 | border: solid 1px #d3d3d3; 203 | border-width: 0 1px 0 0; 204 | box-sizing: border-box; 205 | display: block; 206 | overflow: hidden; 207 | position: relative; 208 | white-space: normal; 209 | } 210 | 211 | .public_fixedDataTableCell_lastChild { 212 | border-width: 0 1px 1px 0; 213 | } 214 | 215 | .public_fixedDataTableCell_highlighted { 216 | background-color: #f4f4f4; 217 | } 218 | 219 | .public_fixedDataTableCell_alignRight { 220 | text-align: right; 221 | } 222 | 223 | .public_fixedDataTableCell_alignCenter { 224 | text-align: center; 225 | } 226 | 227 | .public_fixedDataTableCell_wrap1 { 228 | display: table; 229 | overflow: hidden; 230 | } 231 | 232 | .public_fixedDataTableCell_wrap2 { 233 | display: table-row; 234 | } 235 | 236 | .public_fixedDataTableCell_wrap3 { 237 | display: table-cell; 238 | vertical-align: middle; 239 | } 240 | 241 | .public_fixedDataTableCell_cellContent { 242 | padding: 8px; 243 | } 244 | 245 | .fixedDataTableCell_columnResizerContainer { 246 | position: absolute; 247 | right: 0px; 248 | width: 6px; 249 | z-index: 1; 250 | } 251 | 252 | .fixedDataTableCell_columnResizerContainer:hover { 253 | cursor: ew-resize; 254 | } 255 | 256 | .fixedDataTableCell_columnResizerContainer:hover .fixedDataTableCell_columnResizerKnob { 257 | visibility: visible; 258 | } 259 | 260 | .fixedDataTableCell_columnResizerKnob { 261 | background-color: #0284ff; 262 | position: absolute; 263 | right: 0px; 264 | visibility: hidden; 265 | width: 4px; 266 | } 267 | /** 268 | * Copyright (c) 2015, Facebook, Inc. 269 | * All rights reserved. 270 | * 271 | * This source code is licensed under the BSD-style license found in the 272 | * LICENSE file in the root directory of this source tree. An additional grant 273 | * of patent rights can be found in the PATENTS file in the same directory. 274 | * 275 | * @providesModule fixedDataTableCellGroup 276 | */ 277 | 278 | .fixedDataTableCellGroup_cellGroup { 279 | -webkit-backface-visibility: hidden; 280 | backface-visibility: hidden; 281 | left: 0; 282 | overflow: hidden; 283 | position: absolute; 284 | top: 0; 285 | white-space: nowrap; 286 | } 287 | 288 | .fixedDataTableCellGroup_cellGroup > .public_fixedDataTableCell_main { 289 | display: inline-block; 290 | vertical-align: top; 291 | white-space: normal; 292 | } 293 | 294 | .fixedDataTableCellGroup_cellGroupWrapper { 295 | position: absolute; 296 | top: 0; 297 | } 298 | /** 299 | * Copyright (c) 2015, Facebook, Inc. 300 | * All rights reserved. 301 | * 302 | * This source code is licensed under the BSD-style license found in the 303 | * LICENSE file in the root directory of this source tree. An additional grant 304 | * of patent rights can be found in the PATENTS file in the same directory. 305 | * 306 | * @providesModule fixedDataTableColumnResizerLine 307 | */ 308 | 309 | .fixedDataTableColumnResizerLine_mouseArea { 310 | cursor: ew-resize; 311 | position: absolute; 312 | right: -5px; 313 | width: 12px; 314 | } 315 | 316 | .fixedDataTableColumnResizerLine_main { 317 | border-right: 1px solid #0284ff; 318 | box-sizing: border-box; 319 | position: absolute; 320 | z-index: 10; 321 | } 322 | 323 | .fixedDataTableColumnResizerLine_hiddenElem { 324 | display: none !important; 325 | } 326 | /** 327 | * Copyright (c) 2015, Facebook, Inc. 328 | * All rights reserved. 329 | * 330 | * This source code is licensed under the BSD-style license found in the 331 | * LICENSE file in the root directory of this source tree. An additional grant 332 | * of patent rights can be found in the PATENTS file in the same directory. 333 | * 334 | * @providesModule fixedDataTableRow 335 | */ 336 | 337 | .public_fixedDataTableRow_main { 338 | background-color: #fff; 339 | box-sizing: border-box; 340 | overflow: hidden; 341 | position: absolute; 342 | top: 0; 343 | } 344 | 345 | .fixedDataTableRow_body { 346 | left: 0; 347 | position: absolute; 348 | top: 0; 349 | } 350 | 351 | .public_fixedDataTableRow_highlighted, 352 | .public_fixedDataTableRow_highlighted .public_fixedDataTableCell_main { 353 | background-color: #f6f7f8; 354 | } 355 | 356 | .fixedDataTableRow_fixedColumnsDivider { 357 | -webkit-backface-visibility: hidden; 358 | backface-visibility: hidden; 359 | border-left: solid 1px #d3d3d3; 360 | left: 0; 361 | position: absolute; 362 | top: 0; 363 | width: 0; 364 | } 365 | 366 | .fixedDataTableRow_columnsShadow { 367 | background: 0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAYAAAD5PA/NAAAAFklEQVQIHWPSkNeSBmJhTQVtbiDNCgASagIIuJX8OgAAAABJRU5ErkJggg==) repeat-y; 368 | width: 4px; 369 | } 370 | 371 | .fixedDataTableRow_rowWrapper { 372 | position: absolute; 373 | top: 0; 374 | } 375 | -------------------------------------------------------------------------------- /src/css/_reset.scss: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | html, body, div, span, applet, object, iframe, 6 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 7 | a, abbr, acronym, address, big, cite, code, 8 | del, dfn, em, img, ins, kbd, q, s, samp, 9 | small, strike, strong, sub, sup, tt, var, 10 | b, u, i, center, 11 | dl, dt, dd, ol, ul, li, 12 | fieldset, form, label, legend, 13 | table, caption, tbody, tfoot, thead, tr, th, td, 14 | article, aside, canvas, details, embed, 15 | figure, figcaption, footer, header, hgroup, 16 | menu, nav, output, ruby, section, summary, 17 | time, mark, audio, video { 18 | margin: 0; 19 | padding: 0; 20 | border: 0; 21 | font-size: 100%; 22 | font: inherit; 23 | vertical-align: baseline; 24 | } 25 | /* HTML5 display-role reset for older browsers */ 26 | article, aside, details, figcaption, figure, 27 | footer, header, hgroup, menu, nav, section { 28 | display: block; 29 | } 30 | body { 31 | line-height: 1; 32 | } 33 | ol, ul { 34 | list-style: none; 35 | } 36 | blockquote, q { 37 | quotes: none; 38 | } 39 | blockquote:before, blockquote:after, 40 | q:before, q:after { 41 | content: ''; 42 | content: none; 43 | } 44 | table { 45 | border-collapse: collapse; 46 | border-spacing: 0; 47 | } 48 | -------------------------------------------------------------------------------- /src/css/style.scss: -------------------------------------------------------------------------------- 1 | @import 'reset'; 2 | @import 'fixed-data-table'; 3 | 4 | 5 | body, #client { 6 | max-height:100%; 7 | height:100%; 8 | max-width:100%; 9 | width:100%; 10 | position:fixed; 11 | } 12 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/js/FittedTable.jsx: -------------------------------------------------------------------------------- 1 | 2 | var React = require('react'); 3 | var FixedDataTable = require('fixed-data-table'); 4 | var SetContainerSize = require('./SetContainerSize.jsx'); 5 | 6 | var PropTypes = React.PropTypes; 7 | var Table = FixedDataTable.Table; 8 | var Column = FixedDataTable.Column; 9 | 10 | var TouchScrollArea = require('./TouchScrollArea.jsx'); 11 | 12 | var FittedTable = React.createClass({ 13 | mixins: [SetContainerSize], 14 | 15 | _onContentHeightChange : function(contentHeight) { 16 | var width = 0; 17 | React.Children.forEach(this.props.children, function(child){ 18 | if ('width' in child.props){ 19 | width = width + child.props.width; 20 | } 21 | }); 22 | this.refs.touchScrollArea._onContentDimensionsChange(this.state.width, this.state.height, width, contentHeight); 23 | }, 24 | 25 | handleScroll : function(left, top){ 26 | this.setState({ 27 | top: top, 28 | left: left 29 | }); 30 | }, 31 | 32 | render : function(){ 33 | var controlledScrolling = this.state.left !== undefined || this.state.top !== undefined; 34 | return ( 35 | 36 | 44 | 45 | ); 46 | } 47 | }); 48 | 49 | module.exports = FittedTable; 50 | -------------------------------------------------------------------------------- /src/js/LoadingCellContainer.jsx: -------------------------------------------------------------------------------- 1 | 2 | var React = require('react'); 3 | var ReactComponentWithPureRenderMixin = require('react/lib/ReactComponentWithPureRenderMixin'); 4 | 5 | 6 | var LoadingCellContainer = React.createClass({ 7 | 8 | mixins: [ReactComponentWithPureRenderMixin], 9 | 10 | onDataChange : function(){ 11 | this.forceUpdate(); 12 | }, 13 | 14 | componentWillMount : function(){ 15 | this.props.data.addChangeListener(this.onDataChange); 16 | }, 17 | 18 | componentWillUnmount: function(){ 19 | this.props.data.removeChangeListener(this.onDataChange); 20 | }, 21 | 22 | componentWillReceiveProps: function(nextProps){ 23 | if (this.props && this.props.data){ 24 | this.props.data.removeChangeListener(this.onDataChange); 25 | } 26 | nextProps.data.addChangeListener(this.onDataChange); 27 | }, 28 | 29 | render : function() { 30 | if (this.props.data.data){ 31 | return this.props.renderLoaded(this.props.data.data); 32 | } else { 33 | return this.props.unloaded; 34 | } 35 | } 36 | }); 37 | 38 | module.exports = LoadingCellContainer; 39 | -------------------------------------------------------------------------------- /src/js/LoadingObjectContainer.jsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | var EventEmitter = require('events').EventEmitter; 4 | var assign = require('object-assign'); 5 | 6 | var LoadingObjectContainer = function(){ 7 | var self = this; 8 | 9 | this.data = undefined; 10 | 11 | this.setData = function(data){ 12 | this.data = data; 13 | this.emitChange(); 14 | }; 15 | 16 | this.emitChange = function(){ 17 | self.emit(LoadingObjectContainer.EVENTS.CHANGE); 18 | }; 19 | 20 | this.addChangeListener = function(callback){ 21 | self.on(LoadingObjectContainer.EVENTS.CHANGE, callback); 22 | }; 23 | 24 | this.removeChangeListener = function(callback){ 25 | self.removeListener(LoadingObjectContainer.EVENTS.CHANGE, callback); 26 | }; 27 | 28 | return this; 29 | }; 30 | LoadingObjectContainer.prototype = EventEmitter.prototype; 31 | LoadingObjectContainer.EVENTS = {}; 32 | LoadingObjectContainer.EVENTS.CHANGE = "LOADINGOBJECTCONTAINER.CHANGE"; 33 | 34 | module.exports = LoadingObjectContainer; 35 | -------------------------------------------------------------------------------- /src/js/ObjectData.jsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | var React = require('react'); 4 | var FixedDataTable = require('fixed-data-table'); 5 | 6 | var PropTypes = React.PropTypes; 7 | var Table = FixedDataTable.Table; 8 | var Column = FixedDataTable.Column; 9 | 10 | var LoadingCellContainer = require('./LoadingCellContainer.jsx'); 11 | var ObjectDataListStore = require('./ObjectDataListStore.jsx'); 12 | var FittedTable = require('./FittedTable.jsx'); 13 | 14 | 15 | var renderCell = function(cellData, cellDataKey, rowData, rowIndex, columnData, width){ 16 | var renderLoaded = function(rowData){ 17 | if (cellDataKey == 'thumbnail'){ 18 | return ; 19 | } 20 | return
{rowData[cellDataKey]}
; 21 | }; 22 | 23 | return ( 24 | } 28 | renderLoaded={renderLoaded} /> 29 | ); 30 | }; 31 | 32 | var ObjectData = React.createClass({ 33 | 34 | componentWillMount: function(){ 35 | var self = this; 36 | ObjectDataListStore.addChangeListener(function(){ 37 | self.forceUpdate(); 38 | }); 39 | }, 40 | 41 | render : function() { 42 | return ( 43 | 49 | 54 | 59 | 64 | 69 | 70 | ); 71 | }, 72 | }); 73 | 74 | module.exports = ObjectData; 75 | -------------------------------------------------------------------------------- /src/js/ObjectDataListStore.jsx: -------------------------------------------------------------------------------- 1 | 2 | var $ = require('jquery'); 3 | var EventEmitter = require('events').EventEmitter; 4 | var LoadingObjectContainer = require('./LoadingObjectContainer.jsx'); 5 | 6 | 7 | var ObjectDataListStoreDefinition = function(){ 8 | this.size = undefined; 9 | this.cache = []; 10 | 11 | this.setObjectAt = function(index, data){ 12 | if (data instanceof Array){ 13 | for (var i = 0; i < data.length; i++){ 14 | this.setObjectAt(index + i, data[i]); 15 | } 16 | return; 17 | } 18 | if (!(index in this.cache)){ 19 | this.cache[index] = new LoadingObjectContainer(); 20 | } 21 | this.cache[index].setData(data); 22 | }.bind(this); 23 | 24 | this.getObjectAt = function getObjectAt(index) { 25 | 26 | if (index >= 0 && (index <= this.size || this.size === undefined) && (this.cache.length < index || this.cache[index] === undefined)) { 27 | var self = this; 28 | var limit = 30; 29 | 30 | var url = 'http://jsonplaceholder.typicode.com/photos'; 31 | 32 | var supportsPaging = false; //this fake api doesnt support paging 33 | if (supportsPaging){ 34 | url = url + '?limit=' + limit + '&offset=' + index; 35 | for (var i = 0; i < limit; i++){ 36 | this.cache[index+i] = new LoadingObjectContainer(); 37 | } 38 | } 39 | 40 | $.ajax(url, { 41 | crossDomain: true, 42 | }).then(function(data){ 43 | self.setObjectAt(index, data); 44 | if (self.size === undefined || index + data.length > self.size){ //normally here you would set the size to the full table size, but this fake api doesnt have that information 45 | self.size = index + data.length; 46 | self.emitChange(); 47 | } 48 | }); 49 | } 50 | return index < 0 ? {} : this.cache[index]; 51 | }.bind(this); 52 | 53 | this.getSize = function getSize() { 54 | return this.size === undefined ? 0 : this.size; 55 | }.bind(this); 56 | 57 | this.emitChange = function(){ 58 | this.emit(ObjectDataListStore.EVENTS.CHANGE); 59 | }.bind(this); 60 | 61 | this.addChangeListener = function(callback){ 62 | this.on(ObjectDataListStore.EVENTS.CHANGE, callback); 63 | }.bind(this); 64 | 65 | this.removeChangeListener = function(callback){ 66 | this.removeListener(ObjectDataListStore.EVENTS.CHANGE, callback); 67 | }.bind(this); 68 | 69 | return this; 70 | }; 71 | ObjectDataListStoreDefinition.prototype = EventEmitter.prototype; 72 | var ObjectDataListStore = new ObjectDataListStoreDefinition(); 73 | 74 | ObjectDataListStore.EVENTS = {}; 75 | ObjectDataListStore.EVENTS.CHANGE = "ObjectDataListStore.CHANGE"; 76 | 77 | ObjectDataListStore.getObjectAt(0); 78 | 79 | module.exports = ObjectDataListStore; 80 | -------------------------------------------------------------------------------- /src/js/SetContainerSize.jsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | var detectResize = require('./detect-element-resize.js'); 4 | 5 | var SetContainerSize = { 6 | getInitialState: function(){ 7 | return { 8 | width: 0, 9 | height: 0 10 | }; 11 | }, 12 | 13 | componentDidMount : function(){ 14 | this._onResize(); 15 | detectResize.addResizeListener(this.getDOMNode().parentNode, this._onResize); 16 | }, 17 | 18 | componentWillUnmount : function(){ 19 | detectResize.removeResizeListener(this.getDOMNode().parentNode, this._onResize); 20 | }, 21 | 22 | _onResize : function() { 23 | var node = this.getDOMNode(); 24 | 25 | var borderWidth = node.offsetWidth - node.clientWidth; 26 | var borderHeight = node.offsetHeight - node.clientHeight; 27 | 28 | var width = node.parentNode.offsetWidth - borderWidth; 29 | var height = node.parentNode.offsetHeight - borderHeight; 30 | 31 | this.setState({ 32 | width:width, 33 | height:height 34 | }); 35 | } 36 | }; 37 | 38 | module.exports = SetContainerSize; 39 | 40 | -------------------------------------------------------------------------------- /src/js/TouchScrollArea.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var ZyngaScroller = require('./ZyngaScroller.jsx'); 4 | var TouchableArea = require('./TouchableArea.jsx'); 5 | 6 | var PropTypes = React.PropTypes; 7 | 8 | var cloneWithProps = require('react/lib/cloneWithProps'); 9 | 10 | function isTouchDevice() { 11 | return 'ontouchstart' in document.documentElement // works on most browsers 12 | || 'onmsgesturechange' in window; // works on ie10 13 | }; 14 | 15 | var ScrollArea = React.createClass({ 16 | 17 | componentWillMount : function() { 18 | this.scroller = new ZyngaScroller(isTouchDevice() ? this._handleScroll : this._doNothing); 19 | }, 20 | 21 | render : function() { 22 | if (!isTouchDevice()) { 23 | return this.props.children; 24 | } 25 | 26 | return ( 27 | 28 | {this.props.children} 29 | 30 | ); 31 | }, 32 | 33 | _onContentDimensionsChange : function(tableWidth, tableHeight, contentWidth, contentHeight) { 34 | this.scroller.setDimensions( 35 | tableWidth, 36 | tableHeight, 37 | contentWidth, 38 | contentHeight 39 | ); 40 | }, 41 | 42 | _handleScroll : function(left, top) { 43 | this.props.handleScroll(left, top); 44 | }, 45 | 46 | _doNothing : function(left, top) { 47 | 48 | } 49 | }); 50 | 51 | module.exports = ScrollArea; 52 | -------------------------------------------------------------------------------- /src/js/TouchableArea.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This file provided by Facebook is for non-commercial testing and evaluation 3 | * purposes only. Facebook reserves all rights not expressly granted. 4 | * 5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 10 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | */ 12 | 13 | var React = require('react'); 14 | 15 | var TouchableArea = React.createClass({ 16 | getDefaultProps : function() { 17 | return { 18 | touchable: true 19 | }; 20 | }, 21 | 22 | handleTouchStart : function(e) { 23 | if (!this.props.scroller || !this.props.touchable) { 24 | return; 25 | } 26 | this.props.scroller.doTouchStart(e.touches, e.timeStamp); 27 | e.preventDefault(); 28 | }, 29 | 30 | handleTouchMove : function(e) { 31 | if (!this.props.scroller || !this.props.touchable) { 32 | return; 33 | } 34 | this.props.scroller.doTouchMove(e.touches, e.timeStamp, e.scale); 35 | e.preventDefault(); 36 | }, 37 | 38 | handleTouchEnd : function(e) { 39 | if (!this.props.scroller || !this.props.touchable) { 40 | return; 41 | } 42 | this.props.scroller.doTouchEnd(e.timeStamp); 43 | e.preventDefault(); 44 | }, 45 | 46 | render : function() { 47 | return ( 48 |
53 | {this.props.children} 54 |
55 | ); 56 | } 57 | }); 58 | 59 | module.exports = TouchableArea; 60 | -------------------------------------------------------------------------------- /src/js/ZyngaScroller.jsx: -------------------------------------------------------------------------------- 1 | module.exports = global.Scroller 2 | -------------------------------------------------------------------------------- /src/js/detect-element-resize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Detect Element Resize 3 | * 4 | * https://github.com/sdecima/javascript-detect-element-resize 5 | * Sebastian Decima 6 | * 7 | * version: 0.5.3 8 | **/ 9 | 10 | 11 | var attachEvent = document.attachEvent; 12 | var stylesCreated = false; 13 | 14 | if (!attachEvent) { 15 | var requestFrame = (function(){ 16 | var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || 17 | function(fn){ return window.setTimeout(fn, 20); }; 18 | return function(fn){ return raf(fn); }; 19 | })(); 20 | 21 | var cancelFrame = (function(){ 22 | var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || 23 | window.clearTimeout; 24 | return function(id){ return cancel(id); }; 25 | })(); 26 | 27 | var resetTriggers = function(element){ 28 | var triggers = element.__resizeTriggers__, 29 | expand = triggers.firstElementChild, 30 | contract = triggers.lastElementChild, 31 | expandChild = expand.firstElementChild; 32 | contract.scrollLeft = contract.scrollWidth; 33 | contract.scrollTop = contract.scrollHeight; 34 | expandChild.style.width = expand.offsetWidth + 1 + 'px'; 35 | expandChild.style.height = expand.offsetHeight + 1 + 'px'; 36 | expand.scrollLeft = expand.scrollWidth; 37 | expand.scrollTop = expand.scrollHeight; 38 | }; 39 | 40 | var checkTriggers = function(element){ 41 | return element.offsetWidth != element.__resizeLast__.width || 42 | element.offsetHeight != element.__resizeLast__.height; 43 | } 44 | 45 | var scrollListener = function(e){ 46 | var element = this; 47 | resetTriggers(this); 48 | if (this.__resizeRAF__) cancelFrame(this.__resizeRAF__); 49 | this.__resizeRAF__ = requestFrame(function(){ 50 | if (checkTriggers(element)) { 51 | element.__resizeLast__.width = element.offsetWidth; 52 | element.__resizeLast__.height = element.offsetHeight; 53 | element.__resizeListeners__.forEach(function(fn){ 54 | fn.call(element, e); 55 | }); 56 | } 57 | }); 58 | }; 59 | 60 | /* Detect CSS Animations support to detect element display/re-attach */ 61 | var animation = false, 62 | animationstring = 'animation', 63 | keyframeprefix = '', 64 | animationstartevent = 'animationstart', 65 | domPrefixes = 'Webkit Moz O ms'.split(' '), 66 | startEvents = 'webkitAnimationStart animationstart oAnimationStart MSAnimationStart'.split(' '), 67 | pfx = ''; 68 | { 69 | var elm = document.createElement('fakeelement'); 70 | if( elm.style.animationName !== undefined ) { animation = true; } 71 | 72 | if( animation === false ) { 73 | for( var i = 0; i < domPrefixes.length; i++ ) { 74 | if( elm.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) { 75 | pfx = domPrefixes[ i ]; 76 | animationstring = pfx + 'Animation'; 77 | keyframeprefix = '-' + pfx.toLowerCase() + '-'; 78 | animationstartevent = startEvents[ i ]; 79 | animation = true; 80 | break; 81 | } 82 | } 83 | } 84 | } 85 | 86 | var animationName = 'resizeanim'; 87 | var animationKeyframes = '@' + keyframeprefix + 'keyframes ' + animationName + ' { from { opacity: 0; } to { opacity: 0; } } '; 88 | var animationStyle = keyframeprefix + 'animation: 1ms ' + animationName + '; '; 89 | } 90 | 91 | var createStyles = function() { 92 | if (!stylesCreated) { 93 | //opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360 94 | var css = (animationKeyframes ? animationKeyframes : '') + 95 | '.resize-triggers { ' + (animationStyle ? animationStyle : '') + 'visibility: hidden; opacity: 0; } ' + 96 | '.resize-triggers, .resize-triggers > div, .contract-trigger:before { content: \" \"; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }', 97 | head = document.head || document.getElementsByTagName('head')[0], 98 | style = document.createElement('style'); 99 | 100 | style.type = 'text/css'; 101 | if (style.styleSheet) { 102 | style.styleSheet.cssText = css; 103 | } else { 104 | style.appendChild(document.createTextNode(css)); 105 | } 106 | 107 | head.appendChild(style); 108 | stylesCreated = true; 109 | } 110 | } 111 | 112 | var addResizeListener = function(element, fn){ 113 | if (attachEvent) element.attachEvent('onresize', fn); 114 | else { 115 | if (!element.__resizeTriggers__) { 116 | if (getComputedStyle(element).position == 'static') element.style.position = 'relative'; 117 | createStyles(); 118 | element.__resizeLast__ = {}; 119 | element.__resizeListeners__ = []; 120 | (element.__resizeTriggers__ = document.createElement('div')).className = 'resize-triggers'; 121 | element.__resizeTriggers__.innerHTML = '
' + 122 | '
'; 123 | element.appendChild(element.__resizeTriggers__); 124 | resetTriggers(element); 125 | element.addEventListener('scroll', scrollListener, true); 126 | 127 | /* Listen for a css animation to detect element display/re-attach */ 128 | animationstartevent && element.__resizeTriggers__.addEventListener(animationstartevent, function(e) { 129 | if(e.animationName == animationName) 130 | resetTriggers(element); 131 | }); 132 | } 133 | element.__resizeListeners__.push(fn); 134 | } 135 | }; 136 | 137 | var removeResizeListener = function(element, fn){ 138 | if (attachEvent) element.detachEvent('onresize', fn); 139 | else { 140 | element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1); 141 | if (!element.__resizeListeners__.length) { 142 | element.removeEventListener('scroll', scrollListener); 143 | element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__); 144 | } 145 | } 146 | } 147 | 148 | module.exports = { 149 | addResizeListener : addResizeListener, 150 | removeResizeListener : removeResizeListener 151 | }; 152 | -------------------------------------------------------------------------------- /src/js/main.jsx: -------------------------------------------------------------------------------- 1 | 2 | var React = require('react'); 3 | Object.assign = require('object-assign'); 4 | React.initializeTouchEvents(true); 5 | 6 | var ObjectData = require('./ObjectData.jsx'); 7 | 8 | var App = React.createClass({ 9 | render: function(){ 10 | return ( 11 | 12 | ); 13 | } 14 | }); 15 | 16 | 17 | React.render(, document.getElementById('client')); 18 | 19 | 20 | --------------------------------------------------------------------------------