├── .npmignore ├── .gitignore ├── example ├── webpack.config.js ├── example.html └── example.jsx ├── webpack.config.js ├── HISTORY.md ├── LICENSE ├── package.json ├── index.jsx └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | example 3 | index.jsx 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | index.html 3 | out 4 | node_modules 5 | lib 6 | umd 7 | example/all* 8 | *.log 9 | -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | module: { 3 | loaders: [ 4 | {test: /\.jsx?$/, exclude: /build|node_modules/, loader: 'babel-loader?stage=0'}, 5 | ] 6 | }, 7 | resolve: { 8 | extensions: ['', '.js', '.jsx'] 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /example/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test template 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | output: { 3 | library: 'RadioGroup', 4 | libraryTarget: 'umd' 5 | }, 6 | externals: [ 7 | { 8 | react: { 9 | root: 'React', 10 | commonjs2: 'react', 11 | commonjs: 'react', 12 | amd: 'react' 13 | } 14 | } 15 | ], 16 | module: { 17 | loaders: [ 18 | {test: /\.jsx?$/, exclude: /build|node_modules/, loader: 'babel-loader?stage=0'}, 19 | ] 20 | }, 21 | resolve: { 22 | extensions: ['', '.js', '.jsx'] 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /example/example.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import {RadioGroup, Radio} from '../index.jsx'; 6 | 7 | let App = React.createClass({ 8 | getInitialState() { 9 | return {selectedValue: 'apple'}; 10 | }, 11 | 12 | handleChange(value) { 13 | this.setState({selectedValue: value}); 14 | }, 15 | 16 | render() { 17 | return ( 18 | 22 | 25 | 28 | 31 | 32 | ); 33 | } 34 | }); 35 | 36 | ReactDOM.render(, document.getElementById('content')); 37 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | ### 3.0.3 (March 29th 2018) 2 | - React 16 support 3 | 4 | ### 3.0.0 (June 29th 2016) 5 | - Completely new API: no more function-as-a-child. See Readme for the details 6 | - No new DOM Nodes are created anymore when component gets rerendered. 7 | - Requires React v0.14 or higher 8 | - Both `RadioGroup` and `Radio` have a displayName 9 | 10 | ### 2.2.0 (November 30th 2015) 11 | - `selectedValue` and `onChange` are optional 12 | 13 | ### 2.1.1 (August 17th 2015) 14 | - Allow `selectedValue`'s type to be a boolean. 15 | 16 | ### 2.1.0 (July 26th 2015) 17 | - Library now no longer wraps your children function return with a `div`. It now checks that you return a single component (or `null`) from the function. #18 18 | 19 | ### 2.0.2 (June 14th 2015) 20 | - Make the library work with browser globals. 21 | 22 | ### 2.0.1 (June 14th 2015) 23 | - Compile to UMD so you can import this hackily from a browser script. 24 | 25 | ## 2.0.0 (June 14th 2015) 26 | - API overhaul. See README for the new, cleaner API! 27 | - Drop Bower support. 28 | - Add npm support. 29 | 30 | ## 1.0.0 31 | - Initial release. 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Cheng Lou 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-radio-group", 3 | "version": "3.0.3", 4 | "description": "Better radio buttons.", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "example": "webpack -w -d --config example/webpack.config.js example/example.jsx example/all.js", 8 | "build-umd": "webpack index.jsx umd/index.js", 9 | "build-cjs": "babel index.jsx -d lib", 10 | "prerelease": "npm run build-umd && npm run build-cjs" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/chenglou/react-radio-group.git" 15 | }, 16 | "keywords": [ 17 | "facebook", 18 | "react", 19 | "radio", 20 | "radio-group", 21 | "component", 22 | "react-component" 23 | ], 24 | "dependencies": { 25 | "prop-types": "^15.5.8" 26 | }, 27 | "devDependencies": { 28 | "babel": "^5.5.8", 29 | "babel-core": "^5.2.17", 30 | "babel-loader": "^5.0.0", 31 | "react": ">=0.14.0", 32 | "react-dom": ">=15.5.4", 33 | "webpack": "^1.8.11" 34 | }, 35 | "author": "chenglou ", 36 | "contributors": [ 37 | { 38 | "name": "Jamison Dance", 39 | "email": "jergason@gmail.com", 40 | "url": "http://jamisondance.com" 41 | } 42 | ], 43 | "license": "MIT", 44 | "bugs": { 45 | "url": "https://github.com/chenglou/react-radio-group/issues" 46 | }, 47 | "homepage": "https://github.com/chenglou/react-radio-group", 48 | "peerDependencies": { 49 | "react": ">=0.14.0", 50 | "react-dom": ">=15.5.4" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /index.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | export class Radio extends React.Component { 5 | render() { 6 | const {name, selectedValue, onChange} = this.context.radioGroup; 7 | const optional = {}; 8 | if(selectedValue !== undefined) { 9 | optional.checked = (this.props.value === selectedValue); 10 | } 11 | if(typeof onChange === 'function') { 12 | optional.onChange = onChange.bind(null, this.props.value); 13 | } 14 | 15 | return ( 16 | 23 | ); 24 | } 25 | }; 26 | 27 | Radio.contextTypes = { 28 | radioGroup: PropTypes.object 29 | }; 30 | 31 | export class RadioGroup extends React.Component { 32 | getChildContext() { 33 | const {name, selectedValue, onChange} = this.props; 34 | return { 35 | radioGroup: { 36 | name, selectedValue, onChange 37 | } 38 | } 39 | } 40 | 41 | render() { 42 | const {Component, name, selectedValue, onChange, children, ...rest} = this.props; 43 | return {children}; 44 | } 45 | }; 46 | 47 | RadioGroup.defaultProps = { 48 | Component: "div" 49 | }; 50 | 51 | RadioGroup.propTypes = { 52 | name: PropTypes.string, 53 | selectedValue: PropTypes.oneOfType([ 54 | PropTypes.string, 55 | PropTypes.number, 56 | PropTypes.bool, 57 | ]), 58 | onChange: PropTypes.func, 59 | children: PropTypes.node.isRequired, 60 | Component: PropTypes.oneOfType([ 61 | PropTypes.string, 62 | PropTypes.func, 63 | PropTypes.object, 64 | ]) 65 | }; 66 | 67 | RadioGroup.childContextTypes = { 68 | radioGroup: PropTypes.object 69 | }; 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [React](http://facebook.github.io/react/)-radio-group 2 | 3 | ``` 4 | npm install react-radio-group 5 | ``` 6 | 7 | Then either `import {RadioGroup, Radio} from 'react-radio-group'` or add `node_modules/react-radio-group/umd/index.js` into your HTML file (exports the `RadioGroup` global which contains both, the RadioGroup and Radio component.). 8 | 9 | ## What This Solves 10 | This is your average radio buttons group: 11 | 12 | ```js 13 |
14 | Apple 15 | Orange 16 | Watermelon 17 |
18 | ``` 19 | 20 | A few problems: 21 | - Repetitive fields (`name`, `type`, `checked`, `onChange`). 22 | - Hard to set the checked value. You need to put e.g. `checked={'apple' === this.state.selectedFruitName}` on every input. 23 | - Hard to retrieve the selected value. 24 | 25 | Here's a better version (full example [here](https://github.com/chenglou/react-radio-group/blob/67a2bcdc7f3d0c8cb4d7762f82558d75c9592ea9/example/example.jsx)) 26 | 27 | ```js 28 | 29 | Apple 30 | Orange 31 | Watermelon 32 | 33 | ``` 34 | 35 | Repetitive fields are either lifted onto the `RadioGroup` wrapper or already implicitly set on the `Radio` component, which is a simple wrapper around the radio `input`. 36 | 37 | ## Formal API 38 | #### <RadioGroup /> 39 | Exposes [5 optional props](https://github.com/chenglou/react-radio-group/blob/67a2bcdc7f3d0c8cb4d7762f82558d75c9592ea9/index.jsx#L34-L46): 40 | - `name: String`: what you'd normally put on the radio inputs themselves. 41 | - `selectedValue: String | Number | Boolean`: the currently selected value. This will be used to compare against the values on the `Radio` components to select the right one. 42 | - `onChange: Function`: will be passed the newly selected value. 43 | - `Component: String | React Component`: defaults to `"div"`, defines what tag or component is used for rendering the `RadioGroup` 44 | - `children: Node`: define your `Radio`s and any other components. Each `Radio` component (a thin wrapper around `input`) within a `RadioGroup` will have some fields like `type`, `name` and `checked` prefilled. 45 | 46 | #### <Radio /> 47 | Any prop you pass onto it will be transferred to the actual `input` under the hood. `Radio` components cannot be used outside a `RadioGroup` 48 | 49 | ## License 50 | 51 | [MIT](./LICENSE) 52 | --------------------------------------------------------------------------------