|
1 | 1 | # RangeSlider |
2 | | -A fully customizable high quality react native Slider component backed by custom native iOS and Android views with ability to select range of values. |
| 2 | +A highly optimized and fully customizable pure JS component for value range selection. |
3 | 3 |
|
4 | | -<p align="center"> |
5 | | -<img src="https://raw.githubusercontent.com/githuboftigran/rn-range-slider/master/demo.gif" width="298" height="176"> |
6 | | -</p> |
| 4 | +The component is not re-rendered while user moves the thumb.<br/> |
| 5 | +Even if there is a label, only the label component is re-rendered when values are changed. |
| 6 | + |
| 7 | +RangeSlider uses React Native's Animated library to transform thumbs / label / selected rail.<br/> |
| 8 | +These optimizations help to achieve as much native look & feel as possible using only the JS layer. |
7 | 9 |
|
8 | 10 | <p align="center"> |
9 | | -<a href="https://github.com/githuboftigran/rn-widgets-demo">Demo app</a> |
| 11 | +<img src="https://raw.githubusercontent.com/githuboftigran/rn-range-slider/master/demo.gif" width="369" height="195"> |
10 | 12 | </p> |
11 | 13 |
|
12 | | -## Installation |
13 | | - |
14 | | -1. Add |
| 14 | +#### Version 1 |
| 15 | +The version 1 was using native Android and iOS views.<br/> |
| 16 | +That gives native look & feel in favor of flexibility.<br/> |
| 17 | +You can find the version 1 [here](https://github.com/githuboftigran/rn-range-slider/tree/v1). |
15 | 18 |
|
16 | | - * npm: `npm install --save rn-range-slider` |
17 | | - * yarn: `yarn add rn-range-slider` |
18 | | - |
19 | | -2. Linking |
20 | | - |
21 | | -##### For older React native versions ( < 0.60 ) you need to link the library: |
22 | | - |
23 | | - - Run `react-native link rn-range-slider` |
24 | | - - If linking fails, follow the |
25 | | - [manual linking steps](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#manual-linking) |
| 19 | +## Installation |
26 | 20 |
|
27 | | -##### For newer React native versions ( >= 0.60 ) you need to install pods for iOS: |
28 | | - - cd ios && pod install && cd .. |
29 | | - - For android everything works out of the box |
| 21 | +* npm: `npm install --save rn-range-slider` |
| 22 | +* yarn: `yarn add rn-range-slider` |
30 | 23 |
|
31 | 24 | ## Usage |
32 | 25 |
|
33 | | -```RangeSlider``` should have fixed width and height. |
| 26 | +RangeSlider uses react hooks, so this component doesn't work with React Native versions below 0.59.0 |
34 | 27 |
|
35 | 28 | ``` |
36 | | -import RangeSlider from 'rn-range-slider'; |
37 | | -
|
38 | 29 | ... |
39 | 30 |
|
40 | | -<RangeSlider |
41 | | - style={{width: 160, height: 80}} |
42 | | - gravity={'center'} |
43 | | - min={200} |
44 | | - max={1000} |
45 | | - step={20} |
46 | | - selectionColor="#3df" |
47 | | - blankColor="#f618" |
48 | | - onValueChanged={(low, high, fromUser) => { |
49 | | - this.setState({rangeLow: low, rangeHigh: high}) |
50 | | - }}/> |
51 | | -/> |
| 31 | +import RangeSlider from 'rn-range-slider'; |
52 | 32 |
|
53 | 33 | ... |
54 | | -``` |
55 | | - |
56 | | -### Properties |
57 | | - |
58 | | -Supported color formats are: **#RGB**, **#RGBA**, **#RRGGBB**, **#RRGGBBAA** |
59 | | - |
60 | | - |
61 | | -| Name | Description | Type | Default Value | |
62 | | -|----------|-----------------------|------|:-------------:| |
63 | | -| disabled | If true user won't be able to move the slider | Boolean | **false** | |
64 | | -| rangeEnabled | Slider works as an ordinary slider with 1 control if false | Boolean | **true** | |
65 | | -| valueType | Type of slider values | String<br/><br/>Currently supported values:<br/>- **number**<br/>- **time** | **number** | |
66 | | -| lineWidth | Width of slider's line | Number | **4** | |
67 | | -| thumbRadius | Radius of thumb (including border) | Number | **10** | |
68 | | -| thumbBorderWidth | Border width of thumb | Number | **2** | |
69 | | -| textSize | Size of label text | Number | **16** | |
70 | | -| labelBorderWidth | Border width of label | Number | **2** | |
71 | | -| labelPadding | Padding of label (distance between border and text) | Number | **4** | |
72 | | -| labelBorderRadius | Border radius of label bubble | Number | **4** | |
73 | | -| labelTailHeight | Height of label bubble's tail | Number | **8** | |
74 | | -| labelGapHeight | Gap between label and slider | Number | **4** | |
75 | | -| textFormat | This string will be formatted with active value and shown in thumb.<br/>If `valueType` is set to **time** this prop will be considered as date formatter.<br/>Since this library uses native components and everything is rendered at native side, time on label text will be formatted by [`NSDateFormatter`](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/DataFormatting/Articles/dfDateFormatting10_4.html) for iOS and [`SimpleDateFormat`](https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html) for Android, so make sure you are passing valid format for both platforms. | String<br/>**"Price: %d**" =><br/>"**Price: 75**"<br/>if the current value is 75 | **%d**<br/> (just the number) | |
76 | | -| labelStyle | Style of the label.<br/>Label is not shown if **none** | String<br/><br/>Currently supported values:<br/>- **none**<br/>- **bubble** | **bubble** | |
77 | | -| gravity | Vertical gravity of drawn content | String<br/><br/>Currently supported values:<br/>- **top**<br/>- **bottom**<br/>- **center** | **top** | |
78 | | -| selectionColor | Color of selected part | String | **#4286f4** | |
79 | | -| blankColor | Color of unselected part | String | **#ffffff7f** | |
80 | | -| thumbColor | Color of thumb | String | **#ffffff** | |
81 | | -| thumbBorderColor | Color of thumb's border | String | **#cccccc** | |
82 | | -| labelBackgroundColor | Color label's background | String | **#ff60ad** | |
83 | | -| labelBorderColor | Color label's border | String | **#d13e85** | |
84 | | -| labelTextColor | Color label's text | String | **#ffffff** | |
85 | | -| step | Step of slider. If `valueType` is set to **time**, this prop wil considered as milliseconds. | Number | **1** | |
86 | | - |
87 | | -Props below may have different types depending on `valueType` prop.<br/> |
88 | | -If `valueType` is set to **number**, these props should be `Number`s (integer).<br/> |
89 | | -If `valueType` is set to **time**, these props may be `Number` (integer) or `Date` and if a `Number` is passed the value will be considered as timestamp. |
90 | | - |
91 | | -| Name | Description | Type | Default Value | |
92 | | -|----------|-----------------------|------|:-------------:| |
93 | | -| min | Minimum value of slider | Depends on `valueType` | **0** | |
94 | | -| max | Maximum value of slider | Depends on `valueType` | **100** | |
95 | | -| initialLowValue | Initial value of lower thumb | Depends on `valueType` | **0** | |
96 | | -| initialHighValue | Initial value of higher thumb | Depends on `valueType` | **100** | |
97 | | - |
98 | | -<br/> |
99 | | - |
100 | | -If `initialLowValue` ( or `initialHighValue`) is not provided, it's set to `min` (or `max`). |
101 | 34 |
|
102 | | -### Methods |
| 35 | +const renderThumb = useCallback(() => <Thumb/>, []); |
| 36 | +const renderRail = useCallback(() => <Rail/>, []); |
| 37 | +const renderRailSelected = useCallback(() => <RailSelected/>, []); |
| 38 | +const renderLabel = useCallback(value => <Label text={value}/>, []); |
| 39 | +const renderNotch = useCallback(() => <Notch/>, []); |
| 40 | +const handleValueChange = useCallback((low, high) => { |
| 41 | + setLow(low); |
| 42 | + setHigh(high); |
| 43 | +}, []); |
103 | 44 |
|
104 | | -To call methods of ```RangeSlider``` you need to have a reference to it's instance.<br/> |
105 | | -React native provides 2 ways to do it: |
106 | | - |
107 | | -``` |
108 | | -... |
109 | | -<RangeSlider ref="_rangeSlider" /> |
110 | 45 | ... |
111 | | -this.refs._rangeSlider.setLowValue(42); |
112 | | -... |
113 | | -``` |
114 | 46 |
|
115 | | -or |
| 47 | +<Slider |
| 48 | + style={styles.slider} |
| 49 | + min={0} |
| 50 | + max={100} |
| 51 | + step={1} |
| 52 | + floatingLabel |
| 53 | + renderThumb={renderThumb} |
| 54 | + renderRail={renderRail} |
| 55 | + renderRailSelected={renderRailSelected} |
| 56 | + renderLabel={renderLabel} |
| 57 | + renderNotch={renderNotch} |
| 58 | + onValueChanged={handleValueChange} |
| 59 | +/> |
116 | 60 |
|
117 | | -``` |
118 | | -... |
119 | | -<RangeSlider ref={ component => this._rangeSlider = component } /> |
120 | | -... |
121 | | -this._rangeSlider.setLowValue(42); |
122 | 61 | ... |
123 | 62 | ``` |
124 | 63 |
|
125 | | -#### Available methods |
126 | | - |
127 | | -| Name | Description | Params | |
128 | | -|---|---|---| |
129 | | -| setLowValue | Set low value of slider | value: `Number` (or Date, if `valueType` is set to **time**) | |
130 | | -| setHighValue | Set high value of slider | value: `Number` (or Date, if `valueType` is set to **time**) | |
131 | | - |
132 | | -### Callbacks |
133 | | - |
134 | | -| Name | Description | Params | |
135 | | -|----------|---------------------|--------| |
136 | | -| onValueChanged | A callback to be called when value was changed.<br/><br/>Type of _lowValue_ and _highValue_ will be `Number` if `valueType` is **number** and `Date` if `valueType` is **time**<br/><br/>_fromUser_ parameter is true if the value was changed because of user's interaction (not by calling __setLowValue__ or __setHighValue__ methods). Just like android's [OnSeekbarChangeListener](https://developer.android.com/reference/android/widget/SeekBar.OnSeekBarChangeListener). | lowValue: number<br/><br/>highValue: number<br/><br/>fromUser: boolean | |
137 | | -| onTouchStart | Nothing to explain I think :) | - | |
138 | | -| onTouchEnd | Nothing to explain here too | - | |
| 64 | +### Properties |
139 | 65 |
|
140 | 66 |
|
141 | | -## Known issues |
142 | | -* Label's corner radius is not working on iOS |
143 | | -* Problems with expo (won't fix as I don't take Expo seriously) |
| 67 | +| Name | Description | Type | Default Value | |
| 68 | +| --- | --- | --- | :-------------: | |
| 69 | +| `min` | Minimum value of slider | number | _**required**_ | |
| 70 | +| `max` | Maximum value of slider | number | _**required**_ | |
| 71 | +| `step` | Step of slider | number | `1` | |
| 72 | +| `low` | Low value of slider | number | Initially `min` value will be set if not provided | |
| 73 | +| `high` | High value of slider | number | Initially `max` value will be set if not provided | |
| 74 | +| `floatingLabel` | If set to `true`, labels will not take space in component tree. Instead they will be rendered over the content above the slider (like a small popup). | boolean | `false` | |
| 75 | +| `disableRange` | Slider works as an ordinary slider with 1 control if `true` | boolean | `false` | |
| 76 | +| `allowLabelOverflow` | If set to `true`, labels are allowed to be drawn outside of slider component's bounds.<br/>Otherwise label's edges will never get out of component's edges. | boolean | `false` | |
| 77 | +| `renderThumb` | Should render the thumb. | `() => Node` | _**required**_ | |
| 78 | +| `renderRail` | Should render the "rail" for thumbs.<br/>Rendered component **should** have `flex: 1` style so it won't fill up the whole space. | `() => Node` | _**required**_ | |
| 79 | +| `renderRailSelected` | Should render the selected part of "rail" for thumbs.<br/>Rendered component **should not** have `flex: 1` style so it fills up the whole space. | `() => Node` | _**required**_ | |
| 80 | +| `renderLabel` | Should render label above thumbs.<br/>If no function is passed, no label will be drawn. | `(value: number) => Node` | `undefined` | |
| 81 | +| `renderNotch` | Should render the notch below the label (above the thumbs).<br/>Classic notch is a small triangle below the label.<br/>If `allowLabelOverflow` is not set to true, the notch will continue moving with thumb even if the label has already reached the edge of the component and can't move further.| `() => Node` | `undefined` | |
| 82 | +| `onValueChanged` | Will be called when a value was changed.<br/>If `disableRange` is set to true, the second argument should be ignored. | `(low: number, high: number) => void` | `undefined` | |
| 83 | + |
| 84 | +## A special section about permanent labels. |
| 85 | + |
| 86 | +The label of active thumb is a hint for a user. It's not showing selected values permanently. |
| 87 | +It's a bad UI and UX to have a data for user in a moving label. |
| 88 | +If you need to show current selected values to the user, add Text components to some static place in the screen and set the text based on selected low and high values. |
| 89 | +I didn't and won't add that functionality to this component. |
| 90 | +Any issues about this will be closed immediately. |
0 commit comments