├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── babel.config.js
├── jest.setup.js
├── package.json
├── rollup.config.js
├── src
├── __tests__
│ └── switch.js
├── index.js
├── switch.js
└── utils.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | coverage
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - node
4 | cache: yarn
5 | after_success:
6 | - bash <(curl -s https://codecov.io/bash)
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 pqx Limited
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 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-input-switch
2 |
3 | [](https://www.npmjs.com/package/react-input-switch)
4 | [](https://www.npmjs.com/package/react-input-switch)
5 | [](https://travis-ci.org/swiftcarrot/react-input-switch)
6 | [](https://codecov.io/gh/swiftcarrot/react-input-switch)
7 | [](https://github.com/prettier/prettier)
8 |
9 | React toggle switch component
10 |
11 | ### Installation
12 |
13 | ```sh
14 | npm install react-input-switch --save
15 | yarn add react-input-switch
16 | ```
17 |
18 | ### Demo
19 |
20 | [https://swiftcarrot.dev/react-input-switch/](https://swiftcarrot.dev/react-input-switch/)
21 |
22 | ### Custom styles
23 |
24 | ```javascript
25 |
41 | ```
42 |
43 | ### Controlled example (with hook)
44 |
45 | ```javascript
46 | import React, { useState } from 'react';
47 | import Switch from 'react-input-switch';
48 |
49 | const App = () => {
50 | const [value, setValue] = useState(0);
51 |
52 | return ;
53 | };
54 | ```
55 |
56 | ### Custom on/off value
57 |
58 | The default on/off value is 1/0 and default value is 1. This component will also render a hidden input (``) with current value and the name prop.
59 |
60 | ```javascript
61 | import React, { useState } from 'react';
62 | import Switch from 'react-input-switch';
63 |
64 | const App = () => {
65 | const [value, setValue] = useState('yes');
66 |
67 | return ;
68 | };
69 | ```
70 |
71 | ### License
72 |
73 | MIT
74 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['swiftcarrot']
3 | };
4 |
--------------------------------------------------------------------------------
/jest.setup.js:
--------------------------------------------------------------------------------
1 | import Enzyme from 'enzyme';
2 | import Adapter from 'enzyme-adapter-react-16';
3 |
4 | Enzyme.configure({ adapter: new Adapter() });
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-input-switch",
3 | "version": "2.2.2",
4 | "description": "React toggle switch component",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.esm.js",
7 | "sideEffects": false,
8 | "files": [
9 | "dist"
10 | ],
11 | "scripts": {
12 | "build": "rm -rf dist && NODE_ENV=production rollup -c",
13 | "test": "jest --coverage",
14 | "prepublishOnly": "npm test && npm run build"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/swiftcarrot/react-input-switch.git"
19 | },
20 | "author": "Wang Zuo (https://swiftcarrot.com)",
21 | "license": "MIT",
22 | "keywords": [
23 | "react-component",
24 | "switch",
25 | "input",
26 | "checkbox"
27 | ],
28 | "bugs": {
29 | "url": "https://github.com/swiftcarrot/react-input-switch/issues"
30 | },
31 | "homepage": "https://swiftcarrot.dev/react-input-switch",
32 | "dependencies": {
33 | "@babel/runtime": "^7.5.5",
34 | "@emotion/core": "^10.0.14"
35 | },
36 | "peerDependencies": {
37 | "react": "^16.6.3",
38 | "react-dom": "^16.6.3"
39 | },
40 | "devDependencies": {
41 | "@rollup/plugin-babel": "^5.0.0",
42 | "@rollup/plugin-commonjs": "^11.1.0",
43 | "@rollup/plugin-node-resolve": "^7.1.3",
44 | "babel-jest": "^24.8.0",
45 | "babel-preset-swiftcarrot": "^1.0.0",
46 | "enzyme": "^3.10.0",
47 | "enzyme-adapter-react-16": "^1.14.0",
48 | "jest": "^24.8.0",
49 | "react": "^16.8.6",
50 | "react-dom": "^16.8.6",
51 | "rollup": "^1.17.0"
52 | },
53 | "jest": {
54 | "collectCoverage": true,
55 | "collectCoverageFrom": [
56 | "src/**/*.js"
57 | ],
58 | "setupFiles": [
59 | "/jest.setup.js"
60 | ]
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/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 | const external = id => !id.startsWith('.') && !id.startsWith('/');
8 |
9 | export default [
10 | {
11 | input,
12 | output: {
13 | file: pkg.main,
14 | format: 'cjs'
15 | },
16 | external,
17 | plugins: [
18 | babel({
19 | babelHelpers: 'runtime',
20 | plugins: ['@babel/transform-runtime']
21 | }),
22 | nodeResolve(),
23 | commonjs()
24 | ]
25 | },
26 |
27 | {
28 | input,
29 | output: {
30 | file: pkg.module,
31 | format: 'esm'
32 | },
33 | external,
34 | plugins: [
35 | babel({
36 | babelHelpers: 'runtime',
37 | plugins: [['@babel/transform-runtime', { useESModules: true }]]
38 | }),
39 | nodeResolve(),
40 | commonjs()
41 | ]
42 | }
43 | ];
44 |
--------------------------------------------------------------------------------
/src/__tests__/switch.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { mount } from 'enzyme';
3 | import renderer from 'react-test-renderer';
4 | import Switch from '../switch';
5 |
6 | test('render', () => {
7 | expect(renderer.create().toJSON()).toMatchInlineSnapshot(`
8 |
23 | `);
24 | });
25 |
26 | test('value', () => {
27 | class App extends Component {
28 | state = { value: 0 };
29 |
30 | render() {
31 | return (
32 | this.setState({ value })}
35 | />
36 | );
37 | }
38 | }
39 |
40 | const wrap = mount();
41 |
42 | expect(wrap.find(Switch).props().value).toEqual(0);
43 |
44 | wrap.find('label').simulate('click');
45 | expect(wrap.find(Switch).props().value).toEqual(1);
46 | expect(wrap.find(App).state().value).toEqual(1);
47 |
48 | wrap.find('label').simulate('click');
49 | expect(wrap.find(Switch).props().value).toEqual(0);
50 | expect(wrap.find(App).state().value).toEqual(0);
51 | });
52 |
53 | test('custom on/off with boolean', () => {
54 | class App extends Component {
55 | state = { value: false };
56 |
57 | render() {
58 | return (
59 | this.setState({ value })}
64 | />
65 | );
66 | }
67 | }
68 |
69 | const wrap = mount();
70 |
71 | expect(wrap.find(Switch).props().value).toEqual(false);
72 |
73 | wrap.find('label').simulate('click');
74 | expect(wrap.find(Switch).props().value).toEqual(true);
75 | expect(wrap.find(App).state().value).toEqual(true);
76 |
77 | wrap.find('label').simulate('click');
78 | expect(wrap.find(Switch).props().value).toEqual(false);
79 | expect(wrap.find(App).state().value).toEqual(false);
80 | });
81 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export default from './switch';
2 |
--------------------------------------------------------------------------------
/src/switch.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from '@emotion/core';
3 | import { makeStyles } from './utils';
4 |
5 | const Switch = ({
6 | styles: customStyles,
7 | on,
8 | off,
9 | value,
10 | onChange,
11 | name,
12 | disabled,
13 | ...props
14 | }) => {
15 | const checked = value === on;
16 | const styles = makeStyles(customStyles);
17 |
18 | function handleClick() {
19 | if (onChange) {
20 | onChange(checked ? off : on);
21 | }
22 | }
23 |
24 | return (
25 |
34 | );
35 | };
36 |
37 | Switch.defaultProps = {
38 | value: 1,
39 | on: 1,
40 | off: 0,
41 | disabled: false,
42 | styles: {}
43 | };
44 |
45 | export default Switch;
46 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | export const makeStyles = customStyles => {
2 | return {
3 | container: { ...defaultStyles.container, ...customStyles.container },
4 | containerDisabled: {
5 | ...defaultStyles.containerDisabled,
6 | ...customStyles.containerDisabled
7 | },
8 | track: { ...defaultStyles.track, ...customStyles.track },
9 | trackChecked: {
10 | ...defaultStyles.trackChecked,
11 | ...customStyles.trackChecked
12 | },
13 | button: { ...defaultStyles.button, ...customStyles.button },
14 | buttonChecked: {
15 | ...defaultStyles.buttonChecked,
16 | ...customStyles.buttonChecked
17 | }
18 | };
19 | };
20 |
21 | export const defaultStyles = {
22 | container: {
23 | position: 'relative',
24 | display: 'inline-block',
25 | width: 24,
26 | height: 14,
27 | verticalAlign: 'middle',
28 | cursor: 'pointer',
29 | userSelect: 'none'
30 | },
31 |
32 | containerDisabled: {
33 | opacity: 0.7,
34 | cursor: 'not-allowed'
35 | },
36 |
37 | track: {
38 | position: 'absolute',
39 | top: 0,
40 | left: 0,
41 | right: 0,
42 | bottom: 0,
43 | borderRadius: 7,
44 | backgroundColor: '#cccccc'
45 | },
46 |
47 | trackChecked: {
48 | backgroundColor: '#5e72e4'
49 | },
50 |
51 | button: {
52 | position: 'absolute',
53 | top: 2,
54 | bottom: 2,
55 | right: 11,
56 | left: 2,
57 | backgroundColor: '#fff',
58 | borderRadius: 9,
59 | transition: 'all 100ms ease'
60 | },
61 |
62 | buttonChecked: {
63 | right: 2,
64 | left: 11
65 | }
66 | };
67 |
--------------------------------------------------------------------------------