├── .github └── workflows │ └── release.yml ├── .gitignore ├── .storybook └── main.js ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.js ├── caitou.yml ├── index.d.ts ├── package.json ├── prettier.config.js ├── rollup.config.js ├── src ├── __tests__ │ └── slider.js ├── index.js ├── slider.js ├── styles.js └── utils.js ├── stories └── InputSlider.stories.js └── yarn.lock /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | branches: [master] 5 | jobs: 6 | release: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - run: yarn install 11 | - run: yarn build-storybook 12 | - uses: caitouyun/actions@master 13 | with: 14 | args: caitou deploy --git 15 | env: 16 | CAITOU_TOKEN: ${{ secrets.CAITOU_TOKEN }} 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | coverage 3 | node_modules 4 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ['../stories/**/*.stories.js'], 3 | addons: ['@storybook/addon-actions', '@storybook/addon-links'], 4 | }; 5 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [6.0.1] 6 | 7 | - update react peer deps 8 | 9 | ## [6.0.0] 10 | 11 | - upgrade rollup 12 | - allow mouse down and touch start at any position on the track [#63](https://github.com/swiftcarrot/react-input-slider/pull/63) 13 | 14 | ## [5.1.7] 15 | 16 | - export `InputSliderProps` 17 | 18 | ## [5.1.6] 19 | 20 | - improve styling for thumb 21 | - fix typescript definition 22 | - thumb with pointer cursor by default 23 | 24 | ## [5.1.5] 25 | 26 | - add reverse support 27 | 28 | ## [5.1.4] 29 | 30 | - add type definition file 31 | 32 | ## [5.1.3] 33 | 34 | - add `props.dragStart` support 35 | 36 | ## [5.1.2] 37 | 38 | - format changelog 39 | 40 | ## [5.1.1] 41 | 42 | - event disabled fix 43 | 44 | ## [5.1.0] 45 | 46 | - add `props.disabled` and `styles.disabled` support 47 | 48 | ## [5.0.7] 49 | 50 | - `sideEffects: false` in `package.json` for webpack tree shaking 51 | 52 | ## [5.0.6] 53 | 54 | - Fix wrong thumb's top position calculation 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Wang Zuo 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-slider 2 | 3 | [![npm](https://img.shields.io/npm/v/react-input-slider.svg)](https://www.npmjs.com/package/react-input-slider) 4 | [![npm](https://img.shields.io/npm/dm/react-input-slider.svg)](https://www.npmjs.com/package/react-input-slider) 5 | [![Build Status](https://travis-ci.org/swiftcarrot/react-input-slider.svg?branch=master)](https://travis-ci.org/swiftcarrot/react-input-slider) 6 | [![codecov](https://codecov.io/gh/swiftcarrot/react-input-slider/branch/master/graph/badge.svg)](https://codecov.io/gh/swiftcarrot/react-input-slider) 7 | [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) 8 | 9 | React slider component 10 | 11 | ### Installation 12 | 13 | ```sh 14 | yarn add react-input-slider 15 | npm install react-input-slider --save 16 | ``` 17 | 18 | ### Storybook Demo 19 | 20 | [http://react-input-slider.caitouyun.com](http://react-input-slider.caitouyun.com) 21 | 22 | ### Usage 23 | 24 | ```javascript 25 | import React from 'react'; 26 | import Slider from 'react-input-slider'; 27 | 28 | function App() { 29 | const [state, setState] = useState({ x: 10, y: 10 }); 30 | 31 | return ( 32 |
33 | ({state.x}, {state.y}) 34 | 35 | setState(state => ({ ...state, x }))} 39 | /> 40 | setState(state => ({ ...state, y }))} /> 41 |
42 | ); 43 | } 44 | ``` 45 | 46 | ### Styling 47 | 48 | v5 introduces a new styling api powered by [emotion](https://emotion.sh/) 49 | 50 | ```javascript 51 | 68 | ``` 69 | 70 | ### Props 71 | 72 | | Name | Type | Description | Default | 73 | | ----------- | -------- | ------------------------------------- | ------- | 74 | | axis | string | type of slider (`'x'`, `'y'`, `'xy'`) | `'x'` | 75 | | x | number | value of x | `50` | 76 | | xmax | number | max of x | `100` | 77 | | xmin | number | min of x | `0` | 78 | | y | number | value of y | `50` | 79 | | ymax | number | max of y | `100` | 80 | | ymin | number | min of y | `0` | 81 | | xstep | number | step of x | `1` | 82 | | ystep | number | step of y | `1` | 83 | | onChange | function | handleChange | `null` | 84 | | onDragStart | function | handleDragStart | `null` | 85 | | onDragEnd | function | handleDragEnd | `null` | 86 | | disabled | boolean | input disabled | false | 87 | | xreverse | boolean | reverse on x | false | 88 | | yreverse | boolean | reverse on y | false | 89 | 90 | ### License 91 | 92 | MIT 93 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['swiftcarrot'] 3 | }; 4 | -------------------------------------------------------------------------------- /caitou.yml: -------------------------------------------------------------------------------- 1 | site: react-input-slider 2 | public: storybook-static 3 | force_ssl: true 4 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Interpolation } from '@emotion/serialize'; 3 | 4 | export interface InputSliderProps { 5 | axis?: 'x' | 'y' | 'xy'; 6 | x?: number; 7 | xmax?: number; 8 | xmin?: number; 9 | y?: number; 10 | ymax?: number; 11 | ymin?: number; 12 | xstep?: number; 13 | ystep?: number; 14 | onChange?: (values: { x: number; y: number }) => void; 15 | onDragStart?: (e: MouseEvent) => void; 16 | onDragEnd?: (e: MouseEvent) => void; 17 | disabled?: boolean; 18 | xreverse?: boolean; 19 | yreverse?: boolean; 20 | styles?: { 21 | track?: Interpolation; 22 | active?: Interpolation; 23 | thumb?: Interpolation; 24 | disabled?: Interpolation; 25 | }; 26 | } 27 | 28 | export default class InputSlider extends React.Component {} 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-input-slider", 3 | "version": "6.0.1", 4 | "description": "React input slider component", 5 | "main": "dist/index.cjs.js", 6 | "module": "dist/index.esm.js", 7 | "types": "index.d.ts", 8 | "sideEffects": false, 9 | "scripts": { 10 | "build": "rollup -c", 11 | "test": "NODE_ENV=production jest --coverage", 12 | "prepublishOnly": "npm test && npm run build", 13 | "watch": "rollup -cw", 14 | "storybook": "start-storybook -p 6006", 15 | "build-storybook": "build-storybook" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/swiftcarrot/react-input-slider.git" 20 | }, 21 | "bugs": { 22 | "url": "https://github.com/swiftcarrot/react-input-slider/issues" 23 | }, 24 | "keywords": [ 25 | "input", 26 | "react", 27 | "react-component", 28 | "slider" 29 | ], 30 | "files": [ 31 | "index.d.ts", 32 | "dist" 33 | ], 34 | "author": "Wang Zuo (https://swiftcarrot.com)", 35 | "homepage": "https://swiftcarrot.dev/react-input-slider", 36 | "license": "MIT", 37 | "dependencies": { 38 | "@babel/runtime": "^7.9.2", 39 | "@emotion/core": "^10.0.14" 40 | }, 41 | "devDependencies": { 42 | "@babel/core": "^7.9.0", 43 | "@rollup/plugin-babel": "^5.0.0", 44 | "@rollup/plugin-commonjs": "^11.1.0", 45 | "@rollup/plugin-node-resolve": "^7.1.3", 46 | "@storybook/addon-actions": "^5.3.18", 47 | "@storybook/addon-links": "^5.3.18", 48 | "@storybook/addons": "^5.3.18", 49 | "@storybook/react": "^5.3.18", 50 | "babel-jest": "^25.2.4", 51 | "babel-loader": "^8.1.0", 52 | "babel-preset-swiftcarrot": "^1.1.0", 53 | "jest": "^25.2.4", 54 | "prettier": "^1.19.1", 55 | "react": "^16.8.6", 56 | "react-dom": "^16.8.6", 57 | "react-test-renderer": "^16.8.6", 58 | "rollup": "^2.8.2" 59 | }, 60 | "peerDependencies": { 61 | "react": ">=16", 62 | "react-dom": ">=16" 63 | }, 64 | "jest": { 65 | "collectCoverage": true, 66 | "collectCoverageFrom": [ 67 | "src/**/*.js" 68 | ] 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true 3 | }; 4 | -------------------------------------------------------------------------------- /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__/slider.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import Slider from '../slider'; 4 | 5 | test('x', () => { 6 | const component = renderer.create(); 7 | expect(component.toJSON()).toMatchInlineSnapshot(` 8 |
13 |
21 |
34 |
37 |
38 |
39 | `); 40 | }); 41 | 42 | test('y', () => { 43 | const component = renderer.create(); 44 | expect(component.toJSON()).toMatchInlineSnapshot(` 45 |
50 |
58 |
71 |
74 |
75 |
76 | `); 77 | }); 78 | 79 | test('xy', () => { 80 | const component = renderer.create(); 81 | expect(component.toJSON()).toMatchInlineSnapshot(` 82 |
87 |
91 |
104 |
107 |
108 |
109 | `); 110 | }); 111 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export default from './slider'; 2 | -------------------------------------------------------------------------------- /src/slider.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { jsx } from '@emotion/core'; 3 | import { useRef } from 'react'; 4 | import { getClientPosition } from './utils'; 5 | import defaultStyles from './styles'; 6 | 7 | const Slider = ({ 8 | disabled, 9 | axis, 10 | x, 11 | y, 12 | xmin, 13 | xmax, 14 | ymin, 15 | ymax, 16 | xstep, 17 | ystep, 18 | onChange, 19 | onDragStart, 20 | onDragEnd, 21 | xreverse, 22 | yreverse, 23 | styles: customStyles, 24 | ...props 25 | }) => { 26 | const container = useRef(null); 27 | const handle = useRef(null); 28 | const start = useRef({}); 29 | const offset = useRef({}); 30 | 31 | function getPosition() { 32 | let top = ((y - ymin) / (ymax - ymin)) * 100; 33 | let left = ((x - xmin) / (xmax - xmin)) * 100; 34 | 35 | if (top > 100) top = 100; 36 | if (top < 0) top = 0; 37 | if (axis === 'x') top = 0; 38 | 39 | if (left > 100) left = 100; 40 | if (left < 0) left = 0; 41 | if (axis === 'y') left = 0; 42 | 43 | return { top, left }; 44 | } 45 | 46 | function change({ top, left }) { 47 | if (!onChange) return; 48 | 49 | const { width, height } = container.current.getBoundingClientRect(); 50 | let dx = 0; 51 | let dy = 0; 52 | 53 | if (left < 0) left = 0; 54 | if (left > width) left = width; 55 | if (top < 0) top = 0; 56 | if (top > height) top = height; 57 | 58 | if (axis === 'x' || axis === 'xy') { 59 | dx = (left / width) * (xmax - xmin); 60 | } 61 | 62 | if (axis === 'y' || axis === 'xy') { 63 | dy = (top / height) * (ymax - ymin); 64 | } 65 | 66 | const x = (dx !== 0 ? parseInt(dx / xstep, 10) * xstep : 0) + xmin; 67 | const y = (dy !== 0 ? parseInt(dy / ystep, 10) * ystep : 0) + ymin; 68 | 69 | onChange({ 70 | x: xreverse ? xmax - x + xmin : x, 71 | y: yreverse ? ymax - y + ymin : y 72 | }); 73 | } 74 | 75 | function handleMouseDown(e) { 76 | if (disabled) return; 77 | 78 | e.preventDefault(); 79 | e.stopPropagation(); 80 | e.nativeEvent.stopImmediatePropagation(); 81 | const dom = handle.current; 82 | const clientPos = getClientPosition(e); 83 | 84 | start.current = { 85 | x: dom.offsetLeft, 86 | y: dom.offsetTop 87 | }; 88 | 89 | offset.current = { 90 | x: clientPos.x, 91 | y: clientPos.y 92 | }; 93 | 94 | document.addEventListener('mousemove', handleDrag); 95 | document.addEventListener('mouseup', handleDragEnd); 96 | document.addEventListener('touchmove', handleDrag, { passive: false }); 97 | document.addEventListener('touchend', handleDragEnd); 98 | document.addEventListener('touchcancel', handleDragEnd); 99 | 100 | if (onDragStart) { 101 | onDragStart(e); 102 | } 103 | } 104 | 105 | function getPos(e) { 106 | const clientPos = getClientPosition(e); 107 | const left = clientPos.x + start.current.x - offset.current.x; 108 | const top = clientPos.y + start.current.y - offset.current.y; 109 | 110 | return { left, top }; 111 | } 112 | 113 | function handleDrag(e) { 114 | if (disabled) return; 115 | 116 | e.preventDefault(); 117 | change(getPos(e)); 118 | } 119 | 120 | function handleDragEnd(e) { 121 | if (disabled) return; 122 | 123 | e.preventDefault(); 124 | document.removeEventListener('mousemove', handleDrag); 125 | document.removeEventListener('mouseup', handleDragEnd); 126 | 127 | document.removeEventListener('touchmove', handleDrag, { 128 | passive: false 129 | }); 130 | document.removeEventListener('touchend', handleDragEnd); 131 | document.removeEventListener('touchcancel', handleDragEnd); 132 | 133 | if (onDragEnd) { 134 | onDragEnd(e); 135 | } 136 | } 137 | 138 | function handleTrackMouseDown(e) { 139 | if (disabled) return; 140 | 141 | e.preventDefault(); 142 | const clientPos = getClientPosition(e); 143 | const rect = container.current.getBoundingClientRect(); 144 | 145 | start.current = { 146 | x: clientPos.x - rect.left, 147 | y: clientPos.y - rect.top 148 | }; 149 | 150 | offset.current = { 151 | x: clientPos.x, 152 | y: clientPos.y 153 | }; 154 | 155 | document.addEventListener('mousemove', handleDrag); 156 | document.addEventListener('mouseup', handleDragEnd); 157 | document.addEventListener('touchmove', handleDrag, { passive: false }); 158 | document.addEventListener('touchend', handleDragEnd); 159 | document.addEventListener('touchcancel', handleDragEnd); 160 | 161 | change({ 162 | left: clientPos.x - rect.left, 163 | top: clientPos.y - rect.top 164 | }); 165 | 166 | if (onDragStart) { 167 | onDragStart(e); 168 | } 169 | } 170 | 171 | const pos = getPosition(); 172 | const valueStyle = {}; 173 | if (axis === 'x') valueStyle.width = pos.left + '%'; 174 | if (axis === 'y') valueStyle.height = pos.top + '%'; 175 | if (xreverse) valueStyle.left = 100 - pos.left + '%'; 176 | if (yreverse) valueStyle.top = 100 - pos.top + '%'; 177 | 178 | const handleStyle = { 179 | position: 'absolute', 180 | transform: 'translate(-50%, -50%)', 181 | left: xreverse ? 100 - pos.left + '%' : pos.left + '%', 182 | top: yreverse ? 100 - pos.top + '%' : pos.top + '%' 183 | }; 184 | 185 | if (axis === 'x') { 186 | handleStyle.top = '50%'; 187 | } else if (axis === 'y') { 188 | handleStyle.left = '50%'; 189 | } 190 | 191 | const styles = { 192 | track: { ...defaultStyles[axis].track, ...customStyles.track }, 193 | active: { ...defaultStyles[axis].active, ...customStyles.active }, 194 | thumb: { ...defaultStyles[axis].thumb, ...customStyles.thumb }, 195 | disabled: { ...defaultStyles.disabled, ...customStyles.disabled } 196 | }; 197 | 198 | return ( 199 |
206 |
207 |
217 |
218 |
219 |
220 | ); 221 | }; 222 | 223 | Slider.defaultProps = { 224 | disabled: false, 225 | axis: 'x', 226 | x: 50, 227 | xmin: 0, 228 | xmax: 100, 229 | y: 50, 230 | ymin: 0, 231 | ymax: 100, 232 | xstep: 1, 233 | ystep: 1, 234 | xreverse: false, 235 | yreverse: false, 236 | styles: {} 237 | }; 238 | 239 | export default Slider; 240 | -------------------------------------------------------------------------------- /src/styles.js: -------------------------------------------------------------------------------- 1 | const track = { 2 | position: 'relative', 3 | display: 'inline-block', 4 | backgroundColor: '#ddd', 5 | borderRadius: 5, 6 | userSelect: 'none', 7 | boxSizing: 'border-box' 8 | }; 9 | 10 | const active = { 11 | position: 'absolute', 12 | backgroundColor: '#5e72e4', 13 | borderRadius: 5, 14 | userSelect: 'none', 15 | boxSizing: 'border-box' 16 | }; 17 | 18 | const thumb = { 19 | position: 'relative', 20 | display: 'block', 21 | content: '""', 22 | width: 18, 23 | height: 18, 24 | backgroundColor: '#fff', 25 | borderRadius: '50%', 26 | boxShadow: '0 1px 1px rgba(0,0,0,.5)', 27 | userSelect: 'none', 28 | cursor: 'pointer', 29 | boxSizing: 'border-box' 30 | }; 31 | 32 | const styles = { 33 | x: { 34 | track: { 35 | ...track, 36 | width: 200, 37 | height: 10 38 | }, 39 | 40 | active: { 41 | ...active, 42 | top: 0, 43 | height: '100%' 44 | }, 45 | 46 | thumb: { 47 | ...thumb 48 | } 49 | }, 50 | 51 | y: { 52 | track: { 53 | ...track, 54 | width: 10, 55 | height: 200 56 | }, 57 | 58 | active: { 59 | ...active, 60 | left: 0, 61 | width: '100%' 62 | }, 63 | 64 | thumb: { 65 | ...thumb 66 | } 67 | }, 68 | 69 | xy: { 70 | track: { 71 | position: 'relative', 72 | overflow: 'hidden', 73 | width: 200, 74 | height: 200, 75 | backgroundColor: '#5e72e4', 76 | borderRadius: 0 77 | }, 78 | 79 | active: {}, 80 | 81 | thumb: { 82 | ...thumb 83 | } 84 | }, 85 | 86 | disabled: { 87 | opacity: 0.5 88 | } 89 | }; 90 | 91 | export default styles; 92 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | export function getClientPosition(e) { 2 | const touches = e.touches; 3 | 4 | if (touches && touches.length) { 5 | const finger = touches[0]; 6 | return { 7 | x: finger.clientX, 8 | y: finger.clientY 9 | }; 10 | } 11 | 12 | return { 13 | x: e.clientX, 14 | y: e.clientY 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /stories/InputSlider.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment, useState } from 'react'; 2 | import Slider from '../src'; 3 | 4 | export default { 5 | title: 'InputSlider', 6 | component: Slider 7 | }; 8 | 9 | export const Default = () => { 10 | const [state, setState] = useState({ x: 120, y: 120 }); 11 | 12 | return ( 13 | 14 |
{'x: ' + state.x}
15 |
{'y: ' + state.y}
16 | 26 | setState({ ...state, x })} 32 | /> 33 | setState({ ...state, y })} 39 | /> 40 |
41 | ); 42 | }; 43 | 44 | export const XYExample = () => { 45 | const [state, setState] = useState({ x: 21, y: 73 }); 46 | 47 | return ( 48 | 49 |
{'x: ' + state.x}
50 |
{'y: ' + state.y}
51 | console.log('drag end')} 58 | onChange={setState} 59 | /> 60 |
61 | ); 62 | }; 63 | 64 | export const XExample = () => { 65 | const [state, setState] = useState({ x: 0.3 }); 66 | 67 | return ( 68 | 69 |
{'x: ' + state.x}
70 | setState({ x: parseFloat(x.toFixed(2)) })} 77 | /> 78 |
79 | ); 80 | }; 81 | 82 | export const YExample = () => { 83 | const [state, setState] = useState({ y: 120 }); 84 | 85 | return ( 86 | 87 |
{'y: ' + state.y}
88 | 89 |
90 | ); 91 | }; 92 | 93 | export const ReverseExample = () => { 94 | const [state, setState] = useState({ x: 120, y: 120 }); 95 | 96 | return ( 97 | 98 |
{'x: ' + state.x}
99 |
{'y: ' + state.y}
100 | 112 | setState({ ...state, x })} 118 | xreverse 119 | /> 120 | setState({ ...state, y })} 126 | yreverse 127 | /> 128 |
129 | ); 130 | }; 131 | 132 | export const CustomExample = () => { 133 | const [x, setX] = useState(200); 134 | const [y, setY] = useState(200); 135 | 136 | return ( 137 | 138 | setX(x)} 141 | xmin={100} 142 | xmax={360} 143 | styles={{ 144 | track: { 145 | backgroundColor: 'blue' 146 | }, 147 | active: { 148 | backgroundColor: 'red' 149 | }, 150 | thumb: { 151 | width: 20, 152 | height: 40, 153 | opacity: 0.8, 154 | '&:hover': { 155 | width: 40, 156 | height: 80 157 | } 158 | } 159 | }} 160 | /> 161 | setY(y)} 165 | ymin={100} 166 | ymax={360} 167 | styles={{ 168 | track: { 169 | backgroundColor: 'blue' 170 | }, 171 | active: { 172 | backgroundColor: 'red' 173 | }, 174 | thumb: { 175 | width: 20, 176 | height: 40, 177 | opacity: 0.8 178 | } 179 | }} 180 | /> 181 | 182 | ); 183 | }; 184 | 185 | export const DisabledExample = () => { 186 | const [state, setState] = useState({ x: 10 }); 187 | const [disabled, setDisabled] = useState(true); 188 | 189 | return ( 190 | 191 |
{'x: ' + state.x}
192 |
{'disabled: ' + disabled}
193 | setDisabled(e.target.checked)} 197 | /> 198 |
199 | setState({ x: parseFloat(x.toFixed(2)) })} 204 | /> 205 |
206 |
207 | ); 208 | }; 209 | --------------------------------------------------------------------------------