├── website ├── src │ ├── code │ │ └── Table.js │ ├── components │ │ ├── CodeBlock.css │ │ ├── CodeSandboxLink.module.css │ │ ├── CodeBlock.js │ │ ├── MobileGitHubButton.module.css │ │ ├── MobileNavButton.js │ │ ├── MobileNavButton.module.css │ │ ├── ProfiledExample.module.css │ │ ├── SubMenu.js │ │ ├── ComponentApi.module.css │ │ ├── MobileGitHubButton.js │ │ ├── Nav.module.css │ │ ├── SubMenu.module.css │ │ ├── Nav.js │ │ ├── CodeSandboxLink.js │ │ ├── ProfiledExample.js │ │ └── ComponentApi.js │ ├── routes │ │ ├── api │ │ │ ├── shared.module.css │ │ │ └── Table.js │ │ └── examples │ │ │ ├── Table.js │ │ │ └── shared.module.css │ ├── index.js │ ├── index.css │ ├── App.module.css │ └── App.js ├── .eslintignore ├── now.json ├── config │ ├── jest │ │ ├── babelTransform.js │ │ ├── graphqlTransform.js │ │ ├── cssTransform.js │ │ └── fileTransform.js │ ├── polyfills.js │ ├── utils │ │ └── getLocalIdent.js │ ├── paths.js │ ├── env.js │ ├── webpackDevServer.config.js │ ├── webpack.config.dev.js │ └── webpack.config.prod.js ├── public │ ├── manifest.json │ └── index.html ├── sandboxes │ └── README.md ├── scripts │ ├── test.js │ ├── start.js │ ├── utils │ │ ├── createJestConfig.js │ │ └── verifyPackageTree.js │ └── build.js ├── README.md ├── webpack │ └── codemirror-loader.js └── package.json ├── flow-template ├── .eslintrc ├── .prettierrc ├── .prettierignore ├── README.md ├── src ├── index.js ├── .eslintrc ├── ColumnRenderer.js ├── HeaderRow.js ├── ColumnHeaderRenderer.js ├── Row.js ├── Column.js └── Table.js ├── .eslintignore ├── .flowconfig ├── .travis.yml ├── .editorconfig ├── babel.config.js ├── .gitignore ├── rollup.config.js ├── CHANGELOG.md └── package.json /website/src/code/Table.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/.eslintignore: -------------------------------------------------------------------------------- 1 | src/code -------------------------------------------------------------------------------- /flow-template: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export * from '../src'; 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["react-app", "plugin:prettier/recommended"] 3 | } 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | website/build/* 3 | website/src/code/* 4 | package-lock.json 5 | yarn.lock -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-window-table 2 | 3 | This library is a work in progress. It has not yet been released. -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export { default as Column } from './Column'; 4 | export { default as Table } from './Table'; -------------------------------------------------------------------------------- /website/now.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-window-next", 3 | "alias": "react-window-next", 4 | "public": true 5 | } 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | website/build 3 | website/node_modules 4 | website/src/code 5 | node_modules 6 | package-lock.json 7 | yarn.lock -------------------------------------------------------------------------------- /website/src/components/CodeBlock.css: -------------------------------------------------------------------------------- 1 | .CodeMirror { 2 | height: auto; 3 | } 4 | 5 | .CodeMirror-scroll { 6 | height: auto; 7 | } 8 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | [include] 4 | 5 | [libs] 6 | 7 | [lints] 8 | 9 | [options] 10 | include_warnings=true 11 | 12 | [strict] 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | before_script: 5 | - yarn 6 | script: 7 | - yarn lint 8 | - yarn flow 9 | - yarn test 10 | -------------------------------------------------------------------------------- /website/src/routes/api/shared.module.css: -------------------------------------------------------------------------------- 1 | .CodeBlockWrapper { 2 | display: inline-block; 3 | padding: 1rem; 4 | border-radius: 0.25rem; 5 | background: #263238; /* Codemirror theme */ 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /website/config/jest/babelTransform.js: -------------------------------------------------------------------------------- 1 | const babelJest = require('babel-jest'); 2 | 3 | module.exports = babelJest.createTransformer({ 4 | presets: [require.resolve('babel-preset-react-app')], 5 | }); 6 | -------------------------------------------------------------------------------- /website/config/jest/graphqlTransform.js: -------------------------------------------------------------------------------- 1 | const loader = require('graphql-tag/loader'); 2 | 3 | module.exports = { 4 | process(src) { 5 | return loader.call({ cacheable() {} }, src); 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["react-app", "plugin:prettier/recommended"], 3 | "env": { 4 | "mocha": true 5 | }, 6 | "globals": { 7 | "expect": true, 8 | "spyOn": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /website/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import './index.css'; 5 | import App from './App'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | -------------------------------------------------------------------------------- /website/src/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | #root { 4 | height: 100%; 5 | } 6 | 7 | html { 8 | font-family: sans-serif; 9 | font-size: 12px; 10 | } 11 | 12 | body { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [['@babel/env', { loose: true }], '@babel/flow'], 3 | plugins: [ 4 | ['@babel/proposal-class-properties', { loose: true }], 5 | 'annotate-pure-calls', 6 | ], 7 | }; 8 | -------------------------------------------------------------------------------- /website/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "react-window", 3 | "name": "react-window", 4 | "start_url": "./index.html", 5 | "display": "standalone", 6 | "theme_color": "#000000", 7 | "background_color": "#ffffff" 8 | } 9 | -------------------------------------------------------------------------------- /website/src/App.module.css: -------------------------------------------------------------------------------- 1 | .App { 2 | display: flex; 3 | flex-direction: row; 4 | height: 100%; 5 | } 6 | 7 | .Main { 8 | flex: 1; 9 | overflow: auto; 10 | } 11 | 12 | @media (max-width: 37.5rem) { 13 | .App { 14 | flex-direction: column; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /website/sandboxes/README.md: -------------------------------------------------------------------------------- 1 | This directory contains small examples of how `react-window` is used. 2 | These examples are imported using the Code Sandbox [import from GitHub feature](https://codesandbox.io/docs/importing#import-from-github). 3 | Each folder is a complete standalone project. 4 | -------------------------------------------------------------------------------- /src/ColumnRenderer.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export type ColumnRendererProps = {| 4 | cellData: any, 5 | columnData?: Object, 6 | columnIndex: number, 7 | columnKey: string, 8 | isScrolling: boolean, 9 | rowData: any, 10 | rowIndex: number 11 | |}; 12 | 13 | export default function DefaultHeaderRenderer({ cellData }: ColumnRendererProps) { 14 | return cellData; 15 | } -------------------------------------------------------------------------------- /website/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | // This is a custom Jest transformer turning style imports into empty objects. 2 | // http://facebook.github.io/jest/docs/en/webpack.html 3 | 4 | module.exports = { 5 | process() { 6 | return 'module.exports = {};'; 7 | }, 8 | getCacheKey() { 9 | // The output is always the same. 10 | return 'cssTransform'; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /src/HeaderRow.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type {HeaderEventHandler} from './Table'; 4 | 5 | type Props = {| 6 | additionalAttributes: any, 7 | columns: Array>, 8 | style: Object, 9 | |}; 10 | 11 | export default function HeaderRow(props: Props) { 12 | // TODO Custom merge additionalAttributes.className and additionalAttributes.style 13 | 14 | return 'Header'; 15 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # See https://help.github.com/ignore-files/ for more about ignoring files. 3 | 4 | # dependencies 5 | node_modules 6 | 7 | # builds 8 | build 9 | dist 10 | 11 | # misc 12 | .DS_Store 13 | .env 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | .watchmanconfig 19 | 20 | package-lock.json 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /website/src/components/CodeSandboxLink.module.css: -------------------------------------------------------------------------------- 1 | .CodeSandboxLink { 2 | display: inline-flex; 3 | flex-direction: row; 4 | align-items: center; 5 | color: #8752aa; 6 | } 7 | 8 | .CodeSandboxSvg { 9 | width: 2rem; 10 | height: 2rem; 11 | margin-right: 0.5rem; 12 | } 13 | 14 | .CodeSandboxSvgBackground { 15 | fill: #222; 16 | } 17 | .CodeSandboxSvgForeground { 18 | fill: none; 19 | stroke: #222; 20 | } 21 | -------------------------------------------------------------------------------- /src/ColumnHeaderRenderer.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export type ColumnHeaderRendererProps = {| 4 | additionalAttributes: any, 5 | columnData?: Object, 6 | columnKey: string, 7 | disableSort: boolean, 8 | label: React$Node, 9 | sortBy: string, 10 | sortDirection: 'ASC' | 'DESC' 11 | |}; 12 | 13 | export default function DefaultHeaderRenderer({ label }: ColumnHeaderRendererProps) { 14 | return label; // TODO sort indicators and disabled status. 15 | } -------------------------------------------------------------------------------- /src/Row.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type {ColumnRendererProps} from './ColumnRenderer'; 4 | import type {RowEventHandler} from './Table'; 5 | 6 | type Props = {| 7 | children: Array>, 8 | className: string, 9 | index: number, 10 | isScrolling: boolean, 11 | rowData: any, 12 | rowEvents?: { [eventName: string]: RowEventHandler }, 13 | style: Object, 14 | |}; 15 | 16 | export default function Row(props: Props) { 17 | return 'Row'; 18 | } -------------------------------------------------------------------------------- /website/src/components/CodeBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import 'codemirror/lib/codemirror.css'; 4 | import 'codemirror/theme/material.css'; 5 | 6 | import styles from './CodeBlock.css'; 7 | 8 | const CodeBlock = ({ value }) => ( 9 |
10 |
14 |
15 | ); 16 | 17 | export default CodeBlock; 18 | -------------------------------------------------------------------------------- /website/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | react-window 11 | 12 | 13 | 14 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /website/src/components/MobileGitHubButton.module.css: -------------------------------------------------------------------------------- 1 | .MobileGitHubButton { 2 | border-radius: 5px; 3 | border: 0; 4 | background-color: transparent; 5 | padding: 0; 6 | outline: 0; 7 | height: 2.5rem; 8 | width: 2.5rem; 9 | margin-left: 1rem; 10 | } 11 | .MobileGitHubButton { 12 | display: none; 13 | color: #546e7a; 14 | } 15 | 16 | .MobileGitHubButtonIcon { 17 | width: 2.5rem; 18 | height: 2.5rem; 19 | } 20 | 21 | .MobileGitHubButtonIconPath { 22 | fill: currentColor; 23 | } 24 | 25 | @media (max-width: 37.5rem) { 26 | .MobileGitHubButton { 27 | display: block; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /website/src/components/MobileNavButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './MobileNavButton.module.css'; 4 | 5 | export const MobileNavButton = ({ onClick, isActive }) => ( 6 | 18 | ); 19 | -------------------------------------------------------------------------------- /website/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | // This is a custom Jest transformer turning file imports into filenames. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process(src, filename) { 8 | const assetFilename = JSON.stringify(path.basename(filename)); 9 | 10 | if (filename.match(/\.svg$/)) { 11 | return `module.exports = { 12 | __esModule: true, 13 | default: ${assetFilename}, 14 | ReactComponent: () => ${assetFilename}, 15 | };`; 16 | } 17 | 18 | return `module.exports = ${assetFilename};`; 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /website/src/components/MobileNavButton.module.css: -------------------------------------------------------------------------------- 1 | .MobileNavButton, 2 | .MobileNavButtonActive { 3 | border-radius: 5px; 4 | border: 0; 5 | background-color: transparent; 6 | padding: 0; 7 | outline: 0; 8 | height: 2.5rem; 9 | width: 2.5rem; 10 | margin-right: 1rem; 11 | } 12 | .MobileNavButton { 13 | display: none; 14 | color: #546e7a; 15 | } 16 | .MobileNavButtonActive { 17 | color: #263238; 18 | } 19 | 20 | .MobileNavButtonIcon { 21 | width: 2.5rem; 22 | height: 2.5rem; 23 | } 24 | 25 | .MobileNavButtonIconPath { 26 | fill: currentColor; 27 | } 28 | 29 | @media (max-width: 37.5rem) { 30 | .MobileNavButton { 31 | display: block; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /website/src/routes/api/Table.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { NavLink as Link } from 'react-router-dom'; 3 | import CodeBlock from '../../components/CodeBlock'; 4 | import ComponentApi from '../../components/ComponentApi'; 5 | 6 | import styles from './shared.module.css'; 7 | 8 | export default () => ( 9 | 10 | ); 11 | 12 | const PROPS = [ 13 | { 14 | description: ( 15 | 16 |

17 | TODO 18 |

19 |
20 | ), 21 | isRequired: true, 22 | name: 'children', 23 | type: 'component', 24 | }, 25 | ]; 26 | 27 | const METHODS = []; -------------------------------------------------------------------------------- /website/src/components/ProfiledExample.module.css: -------------------------------------------------------------------------------- 1 | .Row { 2 | width: 100%; 3 | margin-top: 1rem; 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | } 8 | 9 | .ProfilingRow { 10 | width: 100%; 11 | margin-top: 1rem; 12 | display: flex; 13 | align-items: center; 14 | justify-content: space-between; 15 | } 16 | 17 | .Badge { 18 | flex: 0 0 50px; 19 | display: inline-flex; 20 | flex-direction: column; 21 | align-items: center; 22 | padding: 0.25rem; 23 | border-radius: 0.25rem; 24 | background-color: #f8f8f0; 25 | } 26 | 27 | .Label { 28 | font-weight: bold; 29 | } 30 | 31 | .Count { 32 | font-size: 1.25rem; 33 | font-weight: lighter; 34 | color: #263238; 35 | } 36 | -------------------------------------------------------------------------------- /website/src/components/SubMenu.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink as Link } from 'react-router-dom'; 3 | 4 | import styles from './SubMenu.module.css'; 5 | 6 | export const SubMenu = ({ isActiveDark, items, title }) => ( 7 |
8 |

{title}

9 |
    10 | {items.map(({ path, title }) => ( 11 |
  • 12 | 21 | {title} 22 | 23 |
  • 24 | ))} 25 |
26 |
27 | ); 28 | -------------------------------------------------------------------------------- /website/scripts/test.js: -------------------------------------------------------------------------------- 1 | // Do this as the first thing so that any code reading it knows the right env. 2 | process.env.BABEL_ENV = 'test'; 3 | process.env.NODE_ENV = 'test'; 4 | process.env.PUBLIC_URL = ''; 5 | 6 | // Makes the script crash on unhandled rejections instead of silently 7 | // ignoring them. In the future, promise rejections that are not handled will 8 | // terminate the Node.js process with a non-zero exit code. 9 | process.on('unhandledRejection', err => { 10 | throw err; 11 | }); 12 | 13 | // Ensure environment variables are read. 14 | require('../config/env'); 15 | 16 | const jest = require('jest'); 17 | let argv = process.argv.slice(2); 18 | 19 | // Watch unless on CI, in coverage mode, or explicitly running all tests 20 | if ( 21 | !process.env.CI && 22 | argv.indexOf('--coverage') === -1 && 23 | argv.indexOf('--watchAll') === -1 24 | ) { 25 | argv.push('--watch'); 26 | } 27 | 28 | jest.run(argv); 29 | -------------------------------------------------------------------------------- /website/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | 16 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet. 17 | // We don't polyfill it in the browser--this is user's responsibility. 18 | if (process.env.NODE_ENV === 'test') { 19 | require('raf').polyfill(global); 20 | } 21 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import nodeResolve from 'rollup-plugin-node-resolve'; 4 | import pkg from './package.json'; 5 | 6 | const input = './src/index.js'; 7 | 8 | const external = id => !id.startsWith('.') && !id.startsWith('/'); 9 | 10 | export default [ 11 | { 12 | input, 13 | output: { 14 | file: pkg.main, 15 | format: 'cjs', 16 | }, 17 | external, 18 | plugins: [ 19 | babel({ 20 | runtimeHelpers: true, 21 | plugins: ['@babel/transform-runtime'], 22 | }), 23 | nodeResolve(), 24 | commonjs(), 25 | ], 26 | }, 27 | 28 | { 29 | input, 30 | output: { 31 | file: pkg.module, 32 | format: 'esm', 33 | }, 34 | external, 35 | plugins: [ 36 | babel({ 37 | runtimeHelpers: true, 38 | plugins: [['@babel/transform-runtime', { useESModules: true }]], 39 | }), 40 | nodeResolve(), 41 | commonjs(), 42 | ], 43 | }, 44 | ]; 45 | -------------------------------------------------------------------------------- /website/src/components/ComponentApi.module.css: -------------------------------------------------------------------------------- 1 | .ComponentApi { 2 | width: 100%; 3 | display: flex; 4 | flex-direction: row; 5 | flex-wrap: wrap; 6 | align-items: flex-start; 7 | padding: 2rem 2rem 0; 8 | background-color: #fff; 9 | box-sizing: border-box; 10 | } 11 | 12 | .ComponentApiContent { 13 | } 14 | 15 | .ComponentApiHeader { 16 | color: #ec5f67; 17 | font-family: monospace; 18 | font-size: 1.5rem; 19 | margin-top: 0; 20 | } 21 | 22 | .ComponentApiSubHeader { 23 | margin: 1.5rem 0 0.5rem; 24 | font-size: 1rem; 25 | text-transform: uppercase; 26 | color: #537f7e; 27 | } 28 | 29 | .ComponentApiRadioToggle { 30 | margin-left: 0.5rem; 31 | text-transform: none; 32 | font-weight: normal; 33 | font-size: 1rem; 34 | } 35 | 36 | .ComponentApiPropList { 37 | } 38 | 39 | .ComponentApiPropType { 40 | line-height: 2rem; 41 | } 42 | 43 | .ComponentApiPropType { 44 | font-family: monospace; 45 | font-weight: bold; 46 | } 47 | 48 | .ComponentApiPropDefinition { 49 | margin-left: 1rem; 50 | margin-bottom: 1.5rem; 51 | } 52 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the source for [react-window.now.sh](https://react-window.now.sh). 2 | The website structure was created with [`create-react-app`](https://github.com/facebook/create-react-app), although it has since been ejected in order to add custom Webpack loaders. 3 | 4 | ## Local development 5 | 6 | To run this site locally, first make sure you have NPM linked `react-window`: 7 | 8 | ```sh 9 | cd /local/path/to/react-window 10 | npm link 11 | cd ./website 12 | npm link react-window 13 | ``` 14 | 15 | Then run the NPM "start" command in the project root to watch for changes to `react-window`: 16 | 17 | ```sh 18 | cd /local/path/to/react-window 19 | npm run start 20 | ``` 21 | 22 | Lastly, run the NPM "start" command from this directory to run the local webserver: 23 | 24 | ```sh 25 | cd /local/path/to/react-window/website 26 | npm run start 27 | ``` 28 | 29 | ## Deployment 30 | 31 | To deploy this website to Now, use the NPM "deploy" command from this directory: 32 | 33 | ```sh 34 | cd /local/path/to/react-window/website 35 | npm run deploy 36 | ``` 37 | -------------------------------------------------------------------------------- /src/Column.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import ColumnHeaderRenderer from './ColumnHeaderRenderer'; 4 | import ColumnRenderer from './ColumnRenderer'; 5 | 6 | import type {ColumnHeaderRendererProps} from './ColumnHeaderRenderer'; 7 | import type {ColumnRendererProps} from './ColumnRenderer'; 8 | 9 | type DataGetter = ({ columnData: any, key: string, rowData: any }) => any; 10 | 11 | type Prop = {| 12 | children?: React$Element, 13 | className?: string, 14 | dataGetter?: DataGetter, 15 | columnData?: Object, 16 | dataKey: string, 17 | defaultSortDirection?: "ASC" | "DESC", 18 | disableSort?: boolean, 19 | flexGrow?: number, 20 | flexShrink?: number, 21 | headerRenderer: React$Element, 22 | headerAttributes?: any, 23 | maxWidth?: number, 24 | minWidth?: number, 25 | style?: Object, 26 | width: number, 27 | |}; 28 | 29 | export default function Column(props: Prop): null { 30 | return null; 31 | } 32 | 33 | Column.defaultProps = { 34 | children: ColumnRenderer, 35 | dataGetter: ({ columnData, key, rowData }) => rowData[key], 36 | defaultSortDirection: "ASC", 37 | flexGrow: 0, 38 | flexShrink: 1, 39 | headerRenderer: ColumnHeaderRenderer, 40 | }; -------------------------------------------------------------------------------- /website/src/components/MobileGitHubButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './MobileGitHubButton.module.css'; 4 | 5 | export const MobileGitHubButton = () => ( 6 | 10 | 11 | 12 | 26 | 27 | 28 | ); 29 | -------------------------------------------------------------------------------- /website/webpack/codemirror-loader.js: -------------------------------------------------------------------------------- 1 | const CodeMirror = require('codemirror/addon/runmode/runmode.node.js'); 2 | require('codemirror/mode/javascript/javascript.js'); 3 | require('codemirror/mode/jsx/jsx.js'); 4 | 5 | const LANGUAGE = 'jsx'; 6 | 7 | const escapeStyle = style => style.replace(/(^|\s+)/g, '$1cm-'); 8 | const escapeText = string => 9 | string.replace(/[<&]/g, function(ch) { 10 | return ch === '&' ? '&' : '<'; 11 | }); 12 | 13 | module.exports = function loader(content) { 14 | let currentStyle = ''; 15 | let currentText = ''; 16 | let html = ''; 17 | 18 | const appendToLine = () => { 19 | if (currentStyle) { 20 | html += `${escapeText( 21 | currentText 22 | )}`; 23 | } else { 24 | html += escapeText(currentText); 25 | } 26 | }; 27 | 28 | CodeMirror.runMode(content, LANGUAGE, (text, style) => { 29 | if (style !== currentStyle) { 30 | appendToLine(); 31 | currentStyle = style; 32 | currentText = text; 33 | } else { 34 | currentText += text; 35 | } 36 | }); 37 | appendToLine(); 38 | 39 | const lines = html.split(/\n/).map(line => { 40 | if (!line) { 41 | line = ' '; // Don't collapse empty lines 42 | } 43 | return `\n`; 44 | }); 45 | 46 | return `module.exports = ${JSON.stringify(lines.join('\n'))}`; 47 | }; 48 | 49 | module.exports.seperable = true; 50 | -------------------------------------------------------------------------------- /website/src/components/Nav.module.css: -------------------------------------------------------------------------------- 1 | .SideNav { 2 | display: inline-block; 3 | width: 15.5rem; 4 | padding: 0 1rem 1rem; 5 | background-color: #e9eded; 6 | box-sizing: border-box; 7 | max-height: 100vh; 8 | overflow: auto; 9 | } 10 | 11 | .SidebarNavHeaderContainer { 12 | display: flex; 13 | flex: 1; 14 | align-items: center; 15 | margin: 1.5rem 0 0.5rem; 16 | } 17 | 18 | .SideNavHeader { 19 | margin: 0; 20 | font-weight: lighter; 21 | font-size: 2rem; 22 | } 23 | 24 | .SideNavHeaderLink { 25 | text-decoration: none; 26 | color: inherit; 27 | } 28 | 29 | .SidebarNavContent { 30 | display: flex; 31 | flex-direction: column; 32 | overflow: inherit; 33 | transition: none; 34 | overflow: visible; 35 | } 36 | 37 | .SidebarNavContent > div { 38 | flex: 1; 39 | } 40 | 41 | @media (max-width: 25rem) { 42 | .SidebarNavContent { 43 | flex-direction: column; 44 | } 45 | } 46 | 47 | @media (max-width: 37.5rem) { 48 | .SideNav { 49 | display: block; 50 | width: 100%; 51 | } 52 | 53 | .SidebarNavContent { 54 | flex-direction: row; 55 | display: none; 56 | } 57 | 58 | .SideNavHeader { 59 | flex: 1; 60 | text-align: center; 61 | } 62 | 63 | .SidebarNavContentExpanded { 64 | display: flex; 65 | position: absolute; 66 | top: 4rem; 67 | bottom: 0; 68 | left: 0; 69 | right: 0; 70 | overflow: auto; 71 | background-color: #e9eded; 72 | z-index: 10; 73 | padding: 1rem; 74 | display: block; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /website/src/components/SubMenu.module.css: -------------------------------------------------------------------------------- 1 | .SideNavSectionHeader { 2 | margin: 1rem 0; 3 | font-size: 1rem; 4 | font-weight: bold; 5 | text-transform: uppercase; 6 | color: #263238; 7 | } 8 | 9 | .SideNavLinkList { 10 | list-style-type: none; 11 | padding: 0; 12 | margin: 0; 13 | } 14 | 15 | .SideNavLinkListItem { 16 | line-height: 2rem; 17 | padding-left: 1rem; 18 | } 19 | 20 | .SideNavLink { 21 | text-decoration: none; 22 | color: #546e7a; 23 | } 24 | .SideNavLink:hover { 25 | text-decoration: underline; 26 | } 27 | 28 | .SideNavLinkActiveLight, 29 | .SideNavLinkActiveDark { 30 | display: block; 31 | position: relative; 32 | color: #ec5f67; 33 | } 34 | 35 | .SideNavLinkActiveLight:after, 36 | .SideNavLinkActiveDark:after { 37 | display: inline-block; 38 | content: ' '; 39 | height: 0px; 40 | position: absolute; 41 | right: -1rem; 42 | width: 0px; 43 | border-bottom: 0.75rem solid transparent; 44 | border-top: 0.75rem solid transparent; 45 | } 46 | .SideNavLinkActiveLight:after { 47 | border-right: 0.75rem solid #fff; 48 | } 49 | .SideNavLinkActiveDark:after { 50 | border-right: 0.75rem solid #263238; 51 | } 52 | 53 | @media (max-width: 37.5rem) { 54 | .SideNavSectionHeader { 55 | font-size: 1.5rem; 56 | } 57 | 58 | .SideNavLinkListItem { 59 | line-height: 3rem; 60 | } 61 | 62 | .SideNavLink { 63 | font-size: 1.5rem; 64 | font-weight: lighter; 65 | } 66 | 67 | .SideNavLinkActiveLight:after, 68 | .SideNavLinkActiveDark:after { 69 | display: none; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /website/src/routes/examples/Table.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Column, Table } from 'react-window-table'; 3 | import CodeBlock from '../../components/CodeBlock'; 4 | import ProfiledExample from '../../components/ProfiledExample'; 5 | 6 | import CODE from '../../code/Table.js'; 7 | 8 | import styles from './shared.module.css'; 9 | 10 | const items = new Array(500).fill(true).map((_, index) => ({ 11 | foo: index, 12 | bar: index.toString() 13 | })); 14 | 15 | class Item extends PureComponent { 16 | render() { 17 | const { index, style } = this.props; 18 | 19 | return ( 20 |
24 | Item {index} 25 |
26 | ); 27 | } 28 | } 29 | 30 | export default function() { 31 | return ( 32 |
33 |

Basic List

34 |
35 | 39 | items[index]} 44 | rowHeight={30} 45 | width="100%" 46 | > 47 | 48 |
49 |
50 |
51 | 52 |
53 |
54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /website/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { HashRouter as Router, Route, Redirect } from 'react-router-dom'; 3 | import { Nav } from './components/Nav'; 4 | import { SubMenu } from './components/SubMenu'; 5 | 6 | // Routes 7 | import TableExample from './routes/examples/Table'; 8 | import TableApi from './routes/api/Table'; 9 | 10 | import styles from './App.module.css'; 11 | 12 | export default function App() { 13 | return ( 14 | 15 |
16 | 24 |
25 | } 29 | /> 30 | 31 | {EXAMPLE_ROUTES.map(({ component, path }) => ( 32 | 33 | ))} 34 | 35 | {API_ROUTES.map(({ component, path }) => ( 36 | 37 | ))} 38 |
39 |
40 |
41 | ); 42 | } 43 | 44 | const EXAMPLE_ROUTES = [ 45 | { 46 | path: '/examples/table', 47 | title: 'Table', 48 | component: TableExample, 49 | }, 50 | ]; 51 | 52 | const API_ROUTES = [ 53 | { 54 | path: '/api/Table', 55 | title: 'Table', 56 | component: TableApi, 57 | }, 58 | ]; 59 | -------------------------------------------------------------------------------- /website/src/components/Nav.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cs from 'classnames'; 3 | import { MobileNavButton } from './MobileNavButton'; 4 | import { MobileGitHubButton } from './MobileGitHubButton'; 5 | 6 | import styles from './Nav.module.css'; 7 | 8 | export class Nav extends React.Component { 9 | state = { 10 | isExpanded: false, 11 | }; 12 | 13 | render() { 14 | const { title, children } = this.props; 15 | const { isExpanded } = this.state; 16 | 17 | return ( 18 | 46 | ); 47 | } 48 | 49 | collapse = () => { 50 | this.setState({ isExpanded: false }); 51 | }; 52 | 53 | toggleIsExpanded = () => { 54 | this.setState(prevState => ({ isExpanded: !prevState.isExpanded })); 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /website/config/utils/getLocalIdent.js: -------------------------------------------------------------------------------- 1 | const loaderUtils = require('loader-utils'); 2 | 3 | const allowedCharactersFirst = 4 | 'abcdefhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 5 | const allowedCharactersAfter = allowedCharactersFirst + '0123456789-_'; 6 | 7 | const blacklist = [/^ad$/]; 8 | const idents = new Map(); 9 | let indexes = [0]; 10 | 11 | const getNextIdent = key => { 12 | const usedIdents = Array.from(idents.values()); 13 | let ident = ''; 14 | 15 | do { 16 | ident = indexes 17 | .map((i, arrIndex) => { 18 | // Limit the index for allowedCharactersFirst to it's maximum index. 19 | const maxIndexFirst = Math.min(i, allowedCharactersFirst.length - 1); 20 | 21 | return arrIndex === 0 22 | ? allowedCharactersFirst[maxIndexFirst] 23 | : allowedCharactersAfter[i]; 24 | }) 25 | .join(''); 26 | 27 | let i = indexes.length; 28 | while (i--) { 29 | indexes[i] += 1; 30 | 31 | if (indexes[i] === allowedCharactersAfter.length) { 32 | indexes[i] = 0; 33 | 34 | if (i === 0) indexes.push(0); 35 | } else break; 36 | } 37 | } while ( 38 | usedIdents.includes(ident) || 39 | // eslint-disable-next-line no-loop-func 40 | blacklist.some(regex => ident.match(regex)) 41 | ); 42 | 43 | idents.set(key, ident); 44 | return ident; 45 | }; 46 | 47 | module.exports = function getLocalIdent( 48 | context, 49 | localIdentName, 50 | localName, 51 | options 52 | ) { 53 | // Create a hash based on a the file location and class name. Will be unique across a project, and close to globally unique. 54 | const hash = loaderUtils.getHashDigest( 55 | context.resourcePath + localName, 56 | 'md5', 57 | 'base64', 58 | 5 59 | ); 60 | 61 | return idents.get(hash) || getNextIdent(hash); 62 | }; 63 | -------------------------------------------------------------------------------- /website/src/components/CodeSandboxLink.js: -------------------------------------------------------------------------------- 1 | import cn from 'classnames'; 2 | import React from 'react'; 3 | 4 | import styles from './CodeSandboxLink.module.css'; 5 | 6 | const CodeSandboxLink = ({ className, tree = 'master', sandbox }) => { 7 | if (!sandbox) { 8 | return null; 9 | } 10 | 11 | return ( 12 | 18 | 19 | 23 | 27 | 31 | 37 | 46 | 52 | 53 | Try it on CodeSandbox 54 | 55 | ); 56 | }; 57 | 58 | export default CodeSandboxLink; 59 | -------------------------------------------------------------------------------- /website/config/paths.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const url = require('url'); 4 | 5 | // Make sure any symlinks in the project folder are resolved: 6 | // https://github.com/facebook/create-react-app/issues/637 7 | const appDirectory = fs.realpathSync(process.cwd()); 8 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 9 | 10 | const envPublicUrl = process.env.PUBLIC_URL; 11 | 12 | function ensureSlash(path, needsSlash) { 13 | const hasSlash = path.endsWith('/'); 14 | if (hasSlash && !needsSlash) { 15 | return path.substr(path, path.length - 1); 16 | } else if (!hasSlash && needsSlash) { 17 | return `${path}/`; 18 | } else { 19 | return path; 20 | } 21 | } 22 | 23 | const getPublicUrl = appPackageJson => 24 | envPublicUrl || require(appPackageJson).homepage; 25 | 26 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 27 | // "public path" at which the app is served. 28 | // Webpack needs to know it to put the right