├── tests ├── .eslintrc └── index-test.js ├── .gitignore ├── nwb.config.js ├── .travis.yml ├── src ├── LoadingPlaceholder.css └── index.js ├── CONTRIBUTING.md ├── package.json ├── demo └── src │ └── index.js └── README.md /tests/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /demo/dist 3 | /es 4 | /lib 5 | /node_modules 6 | /umd 7 | npm-debug.log* 8 | -------------------------------------------------------------------------------- /nwb.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: 'react-component', 3 | npm: { 4 | esModules: true, 5 | umd: { 6 | global: 'ReactLoadingPlaceholder', 7 | externals: { 8 | react: 'React' 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: 5 | - 6 6 | 7 | before_install: 8 | - npm install codecov.io coveralls 9 | 10 | after_success: 11 | - cat ./coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js 12 | - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js 13 | 14 | branches: 15 | only: 16 | - master 17 | -------------------------------------------------------------------------------- /tests/index-test.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect' 2 | import React from 'react' 3 | import {render, unmountComponentAtNode} from 'react-dom' 4 | 5 | import Component from 'src/' 6 | 7 | describe('Component', () => { 8 | let node 9 | 10 | beforeEach(() => { 11 | node = document.createElement('div') 12 | }) 13 | 14 | afterEach(() => { 15 | unmountComponentAtNode(node) 16 | }) 17 | 18 | it('displays a welcome message', () => { 19 | render(, node, () => { 20 | expect(node.innerHTML).toContain('Welcome to React components') 21 | }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /src/LoadingPlaceholder.css: -------------------------------------------------------------------------------- 1 | @keyframes placeHolderShimmer{ 2 | 0%{ 3 | background-position: -1168px 0 4 | } 5 | 100%{ 6 | background-position: 1168px 0 7 | } 8 | } 9 | 10 | .animated__background { 11 | animation-duration: 2s; 12 | animation-fill-mode: forwards; 13 | animation-iteration-count: infinite; 14 | animation-name: placeHolderShimmer; 15 | animation-timing-function: linear; 16 | background: #f6f7f8; 17 | background: linear-gradient(to right, #eeeeee 8%, #dddddd 18%, #eeeeee 33%); 18 | background-size: 1200px 104px; 19 | height: 96px; 20 | position: relative; 21 | } 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Prerequisites 2 | 3 | [Node.js](http://nodejs.org/) >= v4 must be installed. 4 | 5 | ## Installation 6 | 7 | - Running `npm install` in the components's root directory will install everything you need for development. 8 | 9 | ## Demo Development Server 10 | 11 | - `npm start` will run a development server with the component's demo app at [http://localhost:3000](http://localhost:3000) with hot module reloading. 12 | 13 | ## Running Tests 14 | 15 | - `npm test` will run the tests once. 16 | 17 | - `npm run test:coverage` will run the tests and produce a coverage report in `coverage/`. 18 | 19 | - `npm run test:watch` will run the tests on every change. 20 | 21 | ## Building 22 | 23 | - `npm run build` will build the component for publishing to npm and also bundle the demo app. 24 | 25 | - `npm run clean` will delete built resources. 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-loading-placeholder", 3 | "version": "1.0.6", 4 | "description": "react-loading-placeholder React component", 5 | "main": "lib/index.js", 6 | "module": "es/index.js", 7 | "files": [ 8 | "css", 9 | "es", 10 | "lib", 11 | "umd" 12 | ], 13 | "scripts": { 14 | "build": "nwb build-react-component", 15 | "postbuild": "cp src/LoadingPlaceholder.css lib/ && cp src/LoadingPlaceholder.css es/", 16 | "clean": "nwb clean-module && nwb clean-demo", 17 | "start": "nwb serve-react-demo", 18 | "test": "nwb test-react", 19 | "test:coverage": "nwb test-react --coverage", 20 | "test:watch": "nwb test-react --server" 21 | }, 22 | "dependencies": {}, 23 | "peerDependencies": { 24 | "react": "15.x" 25 | }, 26 | "devDependencies": { 27 | "nwb": "0.18.x", 28 | "react": "^16.0.0", 29 | "react-dom": "^16.0.0" 30 | }, 31 | "author": "", 32 | "homepage": "", 33 | "license": "MIT", 34 | "repository": "", 35 | "keywords": [ 36 | "react-component" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /demo/src/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import {render} from 'react-dom' 3 | 4 | import LoadingPlaceholder from '../../src' 5 | 6 | class Demo extends Component { 7 | 8 | constructor(props) { 9 | super(props); 10 | }; 11 | 12 | render() { 13 | return ( 14 |
15 |

react-loading-placeholder Demo

16 |
17 |

Simple table or list

18 | 19 |
20 | 21 |
22 |

Table with filter

23 | 24 |
25 |
26 | ) 27 | } 28 | } 29 | 30 | render(, document.querySelector('#demo')) 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-loading-placeholder 2 | 3 | [![npm package][npm-badge]][npm] 4 | 5 | Inspired by Facebook Content Placeholder and this article: https://cloudcannon.com/deconstructions/2014/11/15/facebook-content-placeholder-deconstruction.html. Can be used for both tables and lists. 6 | 7 | ![Demo GIF](https://user-images.githubusercontent.com/1689831/30980929-fe6d2e0a-a482-11e7-8b1c-4847190e3e4f.gif) 8 | 9 | 10 | 11 | ## Installation 12 | 13 | ```bash 14 | npm install --save react-loading-placeholder 15 | ``` 16 | 17 | 18 | ## Including it: 19 | ```javascript 20 | import LoadingPlaceholder from 'react-loading-placeholder' 21 | 22 | 23 | ``` 24 | 25 | ## Settings: 26 | 27 | #### Set row height and space between: 28 | ``` 29 | 30 | ``` 31 | 32 | #### Table Layout: 33 | ``` 34 | 35 | ``` 36 | 37 | #### Set filter placeholder size: 38 | ``` 39 | 40 | ``` 41 | 42 | [npm-badge]: https://img.shields.io/npm/v/npm-package.png?style=flat-square 43 | [npm]: https://www.npmjs.com/package/react-loading-placeholder 44 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import styles from './LoadingPlaceholder.css'; 3 | 4 | const defaultMarginTop = 40; 5 | const defaultSpaceBetween = 10; 6 | const defaultHeightOfRows = defaultMarginTop + defaultSpaceBetween; 7 | 8 | const placeholderOptions = { 9 | heightOfRows: defaultHeightOfRows, 10 | marginTop: defaultMarginTop, 11 | spaceBetween: defaultSpaceBetween 12 | }; 13 | 14 | const filterOptions = { 15 | marginTop: 90, 16 | width: 400, 17 | height: 50 18 | } 19 | 20 | class LoadingPlaceholder extends Component { 21 | constructor(props) { 22 | super(props); 23 | }; 24 | 25 | componentWillMount() { 26 | this._setStartValues(); 27 | this._calculateHeight(); 28 | }; 29 | 30 | _setStartValues() { 31 | placeholderOptions.marginTop = defaultMarginTop; 32 | placeholderOptions.spaceBetween = this.props.spaceBetween || defaultSpaceBetween; 33 | if (this.props.heightOfRows) { 34 | placeholderOptions.heightOfRows = this.props.heightOfRows + placeholderOptions.spaceBetween; 35 | placeholderOptions.marginTop = this.props.heightOfRows; 36 | } 37 | else { 38 | placeholderOptions.heightOfRows = defaultHeightOfRows + this.props.spaceBetween || defaultHeightOfRows; 39 | } 40 | 41 | if (this.props.filterOptions) { 42 | filterOptions.width = this.props.filterOptions.width; 43 | filterOptions.height = this.props.filterOptions.height; 44 | if (this.props.filterOptions.marginTop) { 45 | filterOptions.marginTop = this.props.filterOptions.marginTop; 46 | } 47 | } 48 | }; 49 | 50 | _calculateNumberOfRows() { 51 | let _numberOfRows = this.props.numberOfRows; 52 | if (this.props.tableLayout) { 53 | _numberOfRows = _numberOfRows + 1; 54 | } 55 | return _numberOfRows; 56 | }; 57 | 58 | _calculateHeight() { 59 | let _height = placeholderOptions.marginTop; 60 | if (this.props.tableLayout) { 61 | _height = filterOptions.marginTop; 62 | } 63 | for (let i = 1; i < this._calculateNumberOfRows(); i++) { 64 | _height = _height + placeholderOptions.heightOfRows; 65 | }; 66 | return _height; 67 | }; 68 | 69 | _convertToPixels(_int) { 70 | return _int.toString() + 'px'; 71 | }; 72 | 73 | _renderRows() { 74 | let _rows = []; 75 | if (this.props.tableLayout) { 76 | placeholderOptions.marginTop = filterOptions.marginTop; 77 | } 78 | for (let i = 1; i < this._calculateNumberOfRows(); i++) { 79 | let marginTopString = this._convertToPixels(placeholderOptions.marginTop); 80 | _rows.push(
); 81 | placeholderOptions.marginTop = placeholderOptions.marginTop + placeholderOptions.heightOfRows; 82 | } 83 | this._setStartValues(); 84 | return _rows; 85 | }; 86 | 87 | _tableLayout() { 88 | return ( 89 |
90 |
91 |
92 |
93 | ) 94 | }; 95 | 96 | render() { 97 | return ( 98 |
99 | {this.props.tableLayout && this._tableLayout()} 100 | {this._renderRows()} 101 |
102 | ); 103 | } 104 | } 105 | 106 | export default LoadingPlaceholder; 107 | --------------------------------------------------------------------------------