├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── examples ├── App.js └── index.js ├── index.html ├── lib └── index.js ├── package.json ├── server.js ├── src └── index.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "ecmaFeatures": { 3 | "jsx": true, 4 | "modules": true 5 | }, 6 | "env": { 7 | "browser": true, 8 | "node": true 9 | }, 10 | "parser": "babel-eslint", 11 | "rules": { 12 | "quotes": [2, "single"], 13 | "strict": [2, "never"], 14 | "react/jsx-uses-react": 2, 15 | "react/jsx-uses-vars": 2, 16 | "react/react-in-jsx-scope": 2 17 | }, 18 | "plugins": [ 19 | "react" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | .DS_Store 4 | dist 5 | 6 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "newcap": false 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Dan Abramov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | React-Summary 2 | ============= 3 | 4 | Summaries for react components. 5 | 6 | ### Usage 7 | 8 | ```javascript 9 | import Summary from 'react-summary'; 10 | 11 | class App() { 12 | showOthers() { 13 | // others 14 | } 15 | 16 | render() { 17 | return ( 18 | 21 | ) 22 | } 23 | } 24 | ``` 25 | 26 | #### Screenshot 27 | 28 | ![Screenshot](http://i.imgur.com/TCaINON.png 'Screenshot') 29 | 30 | ### Configuration 31 | 32 | The `Summary` component expects the following props. 33 | 34 | | Parameter | Default | | 35 | | :------------- |:-------------------------------| :----- | 36 | | items | [] | The collection that will be aggregated | 37 | | max | 3 | Maximum visible item count | 38 | | text | Object. Explained on the following table. | Translations of visible texts | 39 | | preventSingleRemaining | false | Prints the last item instead of `and 1 more` text | 40 | | onShowOthers | No-op | Callback function for `show others` link | 41 | | itemRenderer | Identity (returns given parameter) | A wrapper function for styling items | 42 | | block | react-summary | The className attribute of component | 43 | 44 | 45 | To styling items, you can provide react components in your collection instead of using itemRenderer. 46 | 47 | Translation strings: 48 | 49 | | Parameter | Default | 50 | | :------------- |:-------------------------------| 51 | | more | 'more' | 52 | | and | 'and' | 53 | | none | 'none' | Translations of visible texts | 54 | 55 | 56 | ### Contributors (You can add your name here in your pull-request) 57 | 58 | - Fatih Erikli 59 | - Burak Can 60 | -------------------------------------------------------------------------------- /examples/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import Summary from '../src'; 4 | 5 | export default class App extends Component { 6 | render() { 7 | return ( 8 |
9 | 13 |
14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sample App 5 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | var _react = require('react'); 10 | 11 | var _react2 = _interopRequireDefault(_react); 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 14 | 15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 16 | 17 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 18 | 19 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 20 | 21 | var Summary = (function (_Component) { 22 | _inherits(Summary, _Component); 23 | 24 | function Summary() { 25 | _classCallCheck(this, Summary); 26 | 27 | return _possibleConstructorReturn(this, Object.getPrototypeOf(Summary).apply(this, arguments)); 28 | } 29 | 30 | _createClass(Summary, [{ 31 | key: 'handleShowOthers', 32 | value: function handleShowOthers(event) { 33 | event.preventDefault(); 34 | 35 | this.props.onShowOthers(); 36 | } 37 | }, { 38 | key: 'renderItems', 39 | value: function renderItems() { 40 | var _this2 = this; 41 | 42 | var _props = this.props; 43 | var items = _props.items; 44 | var text = _props.text; 45 | var max = _props.max; 46 | var itemRenderer = _props.itemRenderer; 47 | var preventSingleRemaining = _props.preventSingleRemaining; 48 | 49 | switch (items.length) { 50 | case 0: 51 | return text.none; 52 | 53 | case 1: 54 | return itemRenderer(items[0]); 55 | 56 | case 2: 57 | case max: 58 | case preventSingleRemaining && max + 1: 59 | 60 | return _react2.default.createElement( 61 | 'div', 62 | null, 63 | items.map(function (item, i) { 64 | return i < items.length - 1 ? _react2.default.createElement( 65 | 'span', 66 | { key: i }, 67 | i > 0 ? text.comma : null, 68 | itemRenderer(item) 69 | ) : null; 70 | }), 71 | ' and ', 72 | itemRenderer(items[items.length - 1]) 73 | ); 74 | 75 | default: 76 | return _react2.default.createElement( 77 | 'div', 78 | null, 79 | items.map(function (item, i) { 80 | return _react2.default.createElement( 81 | 'span', 82 | { key: i }, 83 | i > 0 && i < max ? text.comma : null, 84 | i < max ? itemRenderer(item) : null, 85 | i === max && _react2.default.createElement( 86 | 'span', 87 | null, 88 | ' ', 89 | ' ', 90 | text.and, 91 | ' ', 92 | ' ', 93 | _react2.default.createElement( 94 | 'a', 95 | { href: '#', onClick: _this2.handleShowOthers.bind(_this2) }, 96 | items.length - max, 97 | ' ', 98 | text.more 99 | ) 100 | ) 101 | ); 102 | }) 103 | ); 104 | } 105 | } 106 | }, { 107 | key: 'render', 108 | value: function render() { 109 | var block = this.props.block; 110 | 111 | return _react2.default.createElement( 112 | 'div', 113 | { className: block }, 114 | this.renderItems() 115 | ); 116 | } 117 | }]); 118 | 119 | return Summary; 120 | })(_react.Component); 121 | 122 | Summary.defaultProps = { 123 | block: 'react-summary', 124 | items: [], 125 | max: 3, 126 | text: { 127 | more: 'more', 128 | and: 'and', 129 | none: 'None', 130 | comma: ', ' 131 | }, 132 | onShowOthers: function onShowOthers() {}, 133 | itemRenderer: function itemRenderer(item) { 134 | return item; 135 | }, 136 | preventSingleRemaining: false 137 | }; 138 | exports.default = Summary; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-summary", 3 | "version": "1.0.5", 4 | "description": "React summaries", 5 | "main": "./lib/index.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "transpile": "babel -d lib/ src/" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/adphorus/react-summary.git" 13 | }, 14 | "keywords": [ 15 | "react", 16 | "reactjs", 17 | "summaries" 18 | ], 19 | "author": "Fatih Erikli (http://github.com/fatiherikli)", 20 | "license": "MIT", 21 | "homepage": "https://github.com/adphorus/react-summary", 22 | "devDependencies": { 23 | "babel-core": "^6.0.20", 24 | "babel-eslint": "^4.1.3", 25 | "babel-loader": "^6.0.1", 26 | "babel-preset-es2015": "^6.0.15", 27 | "babel-preset-react": "^6.0.15", 28 | "babel-preset-stage-0": "^6.0.15", 29 | "eslint": "^1.10.3", 30 | "eslint-plugin-react": "^3.6.2", 31 | "react-hot-loader": "^1.3.0", 32 | "webpack": "^1.12.2", 33 | "webpack-dev-server": "^1.12.1" 34 | }, 35 | "dependencies": { 36 | "react": "^0.14.6", 37 | "react-dom": "^0.14.6" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var WebpackDevServer = require('webpack-dev-server'); 3 | var config = require('./webpack.config'); 4 | 5 | new WebpackDevServer(webpack(config), { 6 | publicPath: config.output.publicPath, 7 | hot: true, 8 | historyApiFallback: true 9 | }).listen(3000, 'localhost', function (err, result) { 10 | if (err) { 11 | return console.log(err); 12 | } 13 | 14 | console.log('Listening at http://localhost:3000/'); 15 | }); 16 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class Summary extends Component { 4 | static defaultProps = { 5 | block: 'react-summary', 6 | items: [], 7 | max: 3, 8 | text: { 9 | more: 'more', 10 | and: 'and', 11 | none: 'None', 12 | comma: ', ' 13 | }, 14 | onShowOthers: () => {}, 15 | itemRenderer: (item) => item, 16 | preventSingleRemaining: false, 17 | }; 18 | 19 | handleShowOthers(event) { 20 | event.preventDefault(); 21 | 22 | this.props.onShowOthers(); 23 | } 24 | 25 | renderItems() { 26 | let {items, text, max, 27 | itemRenderer, 28 | preventSingleRemaining} = this.props; 29 | switch (items.length) { 30 | case 0: 31 | return text.none; 32 | 33 | case 1: 34 | return itemRenderer(items[0]); 35 | 36 | case 2: 37 | case max: 38 | case preventSingleRemaining && max + 1: 39 | 40 | return ( 41 |
42 | {items.map((item, i) => ( 43 | i < items.length - 1 ? ( 44 | 45 | {i > 0 ? text.comma : null} 46 | {itemRenderer(item)} 47 | 48 | ) : null 49 | ))} and {itemRenderer(items[items.length - 1])} 50 |
51 | ); 52 | 53 | default: 54 | return ( 55 |
56 | {items.map((item, i) => ( 57 | 58 | {i > 0 && i < max ? text.comma: null} 59 | {i < max ? itemRenderer(item) : null} 60 | {i === max && ( 61 | 62 | {' '} {text.and} {' '} 63 | 64 | {items.length - max} {text.more} 65 | 66 | 67 | )} 68 | 69 | ))} 70 |
71 | ); 72 | } 73 | } 74 | 75 | render() { 76 | let {block} = this.props; 77 | return ( 78 |
79 | {this.renderItems()} 80 |
81 | ); 82 | } 83 | } 84 | 85 | export default Summary; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | module.exports = { 5 | devtool: 'eval', 6 | entry: [ 7 | 'webpack-dev-server/client?http://localhost:3000', 8 | 'webpack/hot/only-dev-server', 9 | './examples/index' 10 | ], 11 | output: { 12 | path: path.join(__dirname, 'dist'), 13 | filename: 'bundle.js', 14 | publicPath: '/static/' 15 | }, 16 | plugins: [ 17 | new webpack.HotModuleReplacementPlugin() 18 | ], 19 | module: { 20 | loaders: [{ 21 | test: /\.js$/, 22 | loaders: ['react-hot', 'babel'], 23 | include: [ 24 | path.join(__dirname, 'examples'), 25 | path.join(__dirname, 'src') 26 | ] 27 | }] 28 | } 29 | }; 30 | --------------------------------------------------------------------------------