commit c3b08e2ef6990ab9568df153b6c9cb0b9711080e Author: saleel Date: Sat Apr 8 01:01:27 2017 +0400 Initial Commit diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..d81eb3d --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,11 @@ +module.exports = { + "extends": "airbnb", + "plugins": [ + "react", + "jsx-a11y", + "import" + ], + "rules": { + "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }] + } +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..47be6b3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Logs +logs +*.log +npm-debug.log* + +# Dependency directories +node_modules + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..a269fc9 --- /dev/null +++ b/.npmignore @@ -0,0 +1,16 @@ +# Logs +logs +*.log +npm-debug.log* + +# Dependency directories +node_modules + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# Screenshots +screenshots diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f8b8c3b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Saleel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..89c7f3c --- /dev/null +++ b/README.md @@ -0,0 +1,136 @@ +# React Native Super Grid + +Responsive Grid View for React Native. + + + +## Getting Started + +This component renders a Grid View that adapts itself to various screen resolutions. + +Instead of passing an itemPerRow argument, you pass ```itemWidth``` and each item will be rendered with a width equal to or more than (to fill the screen) the given width. + +Internally, this component use the native [ListView](https://facebook.github.io/react-native/docs/listview.html). + + +### Installing + +You can install the package via npm. + +``` +npm install react-native-super-grid +``` + + +### Usage +``` +import GridView from 'react-native-super-grid'; +``` +``` + ({item})} +/> +``` + +#### Properties + +| Property | Type | Default Value | Description | +|---|---|---|---| +| renderItem | Function | | Function to render each object. Should return a react native component. | +| items | Array | | Items to be rendered. renderItem will be called with each item in this array. | | +| itemWidth | Number | 120 | Minimum width for each item in pixels (virtual). | +| fixed | Boolean | false | If true, the exact ```itemWidth``` will be used and won't be adjusted to fit the screen. | +| spacing | Number | 10 | Spacing between each item. | +| style | [ListView](https://facebook.github.io/react-native/docs/listview.html) styles (Object) | | Styles for the container. Styles for an item should be applied inside ```renderItem```. | + + + +## Example +``` +import React, { Component } from 'react'; +import { StyleSheet, View, Text } from 'react-native'; +import GridView from 'react-native-super-grid'; + +export default class Example extends Component { + render() { + // Taken from https://flatuicolors.com/ + const items = [ + { name: 'TURQUOISE', code: '#1abc9c' }, { name: 'EMERALD', code: '#2ecc71' }, + { name: 'PETER RIVER', code: '#3498db' }, { name: 'AMETHYST', code: '#9b59b6' }, + { name: 'WET ASPHALT', code: '#34495e' }, { name: 'GREEN SEA', code: '#16a085' }, + { name: 'NEPHRITIS', code: '#27ae60' }, { name: 'BELIZE HOLE', code: '#2980b9' }, + { name: 'WISTERIA', code: '#8e44ad' }, { name: 'MIDNIGHT BLUE', code: '#2c3e50' }, + { name: 'SUN FLOWER', code: '#f1c40f' }, { name: 'CARROT', code: '#e67e22' }, + { name: 'ALIZARIN', code: '#e74c3c' }, { name: 'CLOUDS', code: '#ecf0f1' }, + { name: 'CONCRETE', code: '#95a5a6' }, { name: 'ORANGE', code: '#f39c12' }, + { name: 'PUMPKIN', code: '#d35400' }, { name: 'POMEGRANATE', code: '#c0392b' }, + { name: 'SILVER', code: '#bdc3c7' }, { name: 'ASBESTOS', code: '#7f8c8d' }, + ]; + + return ( + ( + + {item.name} + {item.code} + + )} + /> + ); + } +} + +const styles = StyleSheet.create({ + gridView: { + paddingTop: 25, + flex: 1, + }, + itemContainer: { + justifyContent: 'flex-end', + borderRadius: 5, + padding: 10, + height: 150, + }, + itemName: { + fontSize: 16, + color: '#fff', + fontWeight: '600', + }, + itemCode: { + fontWeight: '600', + fontSize: 12, + color: '#fff', + }, +}); +``` + +| ![iPhone6 Portrait](/screenshots/iphone6_portrait.png?raw=true "iPhone6 Portrait")| ![iPhone6 Landscape](/screenshots/iphone6_landscape.png?raw=true "iPhone6 Landscape") | +|:---:|:---:| +| iPhone6 Portrait | iPhone6 Landscape | + +| ![iPad Air 2 Portrait](/screenshots/ipadair2_portrait.png?raw=true "iPad Air 2 Portrait") | ![iPad Air 2 Landscape](/screenshots/ipadair2_landscape.png?raw=true "iPad Air 2 Landscape") | +|:---:|:---:| +| iPad Air 2 Portrait | iPad Air 2 Landscape | + +| ![Android Portrait](/screenshots/android_portrait.png?raw=true "Android Portrait") | ![Android Landscape](/screenshots/android_landscape.png?raw=true "Android Landscape") | +|:---:|:---:| +| Android Portrait | Android Landscape | + + + +## License + +This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details. + + + +## Acknowledgments + +Colors in the example from https://flatuicolors.com/. + +Screenshot Mockup generated from https://mockuphone.com. diff --git a/index.js b/index.js new file mode 100644 index 0000000..66de6b0 --- /dev/null +++ b/index.js @@ -0,0 +1,109 @@ +import React, { Component, PropTypes } from 'react'; +import { View, ListView, Dimensions } from 'react-native'; +import { chunkArray } from './utils'; + +class SuperGrid extends Component { + constructor(props) { + super(props); + this.renderRow = this.renderRow.bind(this); + this.onLayout = this.onLayout.bind(this); + this.getDimensions = this.getDimensions.bind(this); + this.state = this.getDimensions(); + } + + onLayout(e) { + const { width } = e.nativeEvent.layout || {}; + this.setState({ + ...this.getDimensions(width), + }); + } + + getDimensions(lvWidth) { + const { itemWidth, spacing, fixed } = this.props; + const totalWidth = lvWidth || Dimensions.get('window').width; + + const itemTotalWidth = itemWidth + spacing; + const availableWidth = totalWidth - spacing; // One spacing extra + const itemsPerRow = Math.floor(availableWidth / itemTotalWidth); + const containerWidth = availableWidth / itemsPerRow; + + return { + itemWidth, + spacing, + itemsPerRow, + containerWidth, + fixed, + }; + } + + renderRow(data, sectionId, rowId) { + const { itemWidth, spacing, containerWidth, fixed } = this.state; + + const rowStyle = { + flexDirection: 'row', + paddingLeft: spacing, + paddingBottom: spacing, + }; + const columnStyle = { + flexDirection: 'column', + justifyContent: 'center', + width: containerWidth, + paddingRight: spacing, + }; + let itemStyle = { }; + if (fixed) { + itemStyle = { + width: itemWidth, + alignSelf: 'center', + }; + } + + return ( + + {(data || []).map((item, i) => ( + + + {this.props.renderItem(item)} + + + ))} + + ); + } + + render() { + const { items, style, renderItem, spacing, fixed, itemWidth, ...props } = this.props; + const { itemsPerRow } = this.state; + + const rows = chunkArray(items, itemsPerRow); + const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }); + + return ( + + ); + } +} + +SuperGrid.propTypes = { + renderItem: PropTypes.func.isRequired, + items: PropTypes.arrayOf(PropTypes.any).isRequired, + itemWidth: PropTypes.number, + fixed: PropTypes.bool, + spacing: PropTypes.number, + style: View.propTypes.style, +}; + +SuperGrid.defaultProps = { + fixed: false, + itemWidth: 120, + spacing: 10, + style: {}, +}; + +export default SuperGrid; diff --git a/package.json b/package.json new file mode 100644 index 0000000..2e8b3ba --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "react-native-super-grid", + "version": "1.0.2", + "description": "Responsive Grid View for React Native", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/saleel97/react-native-super-grid.git" + }, + "peerDependencies": { + "react-native": ">=0.26.0" + }, + "homepage": "https://github.com/saleel97/react-native-super-grid", + "keywords": [ + "react", + "native", + "responsive", + "super", + "grid", + "view" + ], + "author": "Saleel", + "license": "MIT", + "devDependencies": { + "eslint": "^3.19.0", + "eslint-config-airbnb": "^14.1.0", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-jsx-a11y": "^4.0.0", + "eslint-plugin-react": "^6.10.3" + } +} diff --git a/screenshots/android_landscape.png b/screenshots/android_landscape.png new file mode 100644 index 0000000..4af3ee5 Binary files /dev/null and b/screenshots/android_landscape.png differ diff --git a/screenshots/android_portrait.png b/screenshots/android_portrait.png new file mode 100644 index 0000000..81ec3d7 Binary files /dev/null and b/screenshots/android_portrait.png differ diff --git a/screenshots/ipadair2_landscape.png b/screenshots/ipadair2_landscape.png new file mode 100644 index 0000000..74480a5 Binary files /dev/null and b/screenshots/ipadair2_landscape.png differ diff --git a/screenshots/ipadair2_portrait.png b/screenshots/ipadair2_portrait.png new file mode 100644 index 0000000..9dfccc0 Binary files /dev/null and b/screenshots/ipadair2_portrait.png differ diff --git a/screenshots/iphone6_landscape.png b/screenshots/iphone6_landscape.png new file mode 100644 index 0000000..4fa84bb Binary files /dev/null and b/screenshots/iphone6_landscape.png differ diff --git a/screenshots/iphone6_portrait.png b/screenshots/iphone6_portrait.png new file mode 100644 index 0000000..96f2cf3 Binary files /dev/null and b/screenshots/iphone6_portrait.png differ diff --git a/utils.js b/utils.js new file mode 100644 index 0000000..d4fbe5b --- /dev/null +++ b/utils.js @@ -0,0 +1,13 @@ +// eslint-disable-next-line import/prefer-default-export +export function chunkArray(array, size) { + return array.reduce((acc, val) => { + if (acc.length === 0) acc.push([]); + const last = acc[acc.length - 1]; + if (last.length < size) { + last.push(val); + } else { + acc.push([val]); + } + return acc; + }, []); +}