Slider.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @format
  8. * @flow strict-local
  9. */
  10. 'use strict';
  11. const Platform = require('../../Utilities/Platform');
  12. import SliderNativeComponent from './SliderNativeComponent';
  13. const React = require('react');
  14. const StyleSheet = require('../../StyleSheet/StyleSheet');
  15. import type {ImageSource} from '../../Image/ImageSource';
  16. import type {ViewStyleProp} from '../../StyleSheet/StyleSheet';
  17. import type {ColorValue} from '../../StyleSheet/StyleSheetTypes';
  18. import type {ViewProps} from '../View/ViewPropTypes';
  19. import type {SyntheticEvent} from '../../Types/CoreEventTypes';
  20. type Event = SyntheticEvent<
  21. $ReadOnly<{|
  22. value: number,
  23. /**
  24. * Android Only.
  25. */
  26. fromUser?: boolean,
  27. |}>,
  28. >;
  29. type IOSProps = $ReadOnly<{|
  30. /**
  31. * Assigns a single image for the track. Only static images are supported.
  32. * The center pixel of the image will be stretched to fill the track.
  33. */
  34. trackImage?: ?ImageSource,
  35. /**
  36. * Assigns a minimum track image. Only static images are supported. The
  37. * rightmost pixel of the image will be stretched to fill the track.
  38. */
  39. minimumTrackImage?: ?ImageSource,
  40. /**
  41. * Assigns a maximum track image. Only static images are supported. The
  42. * leftmost pixel of the image will be stretched to fill the track.
  43. */
  44. maximumTrackImage?: ?ImageSource,
  45. /**
  46. * Sets an image for the thumb. Only static images are supported.
  47. */
  48. thumbImage?: ?ImageSource,
  49. |}>;
  50. type Props = $ReadOnly<{|
  51. ...ViewProps,
  52. ...IOSProps,
  53. /**
  54. * Used to style and layout the `Slider`. See `StyleSheet.js` and
  55. * `DeprecatedViewStylePropTypes.js` for more info.
  56. */
  57. style?: ?ViewStyleProp,
  58. /**
  59. * Initial value of the slider. The value should be between minimumValue
  60. * and maximumValue, which default to 0 and 1 respectively.
  61. * Default value is 0.
  62. *
  63. * *This is not a controlled component*, you don't need to update the
  64. * value during dragging.
  65. */
  66. value?: ?number,
  67. /**
  68. * Step value of the slider. The value should be
  69. * between 0 and (maximumValue - minimumValue).
  70. * Default value is 0.
  71. */
  72. step?: ?number,
  73. /**
  74. * Initial minimum value of the slider. Default value is 0.
  75. */
  76. minimumValue?: ?number,
  77. /**
  78. * Initial maximum value of the slider. Default value is 1.
  79. */
  80. maximumValue?: ?number,
  81. /**
  82. * The color used for the track to the left of the button.
  83. * Overrides the default blue gradient image on iOS.
  84. */
  85. minimumTrackTintColor?: ?ColorValue,
  86. /**
  87. * The color used for the track to the right of the button.
  88. * Overrides the default blue gradient image on iOS.
  89. */
  90. maximumTrackTintColor?: ?ColorValue,
  91. /**
  92. * The color used to tint the default thumb images on iOS, or the
  93. * color of the foreground switch grip on Android.
  94. */
  95. thumbTintColor?: ?ColorValue,
  96. /**
  97. * If true the user won't be able to move the slider.
  98. * Default value is false.
  99. */
  100. disabled?: ?boolean,
  101. /**
  102. * Callback continuously called while the user is dragging the slider.
  103. */
  104. onValueChange?: ?(value: number) => void,
  105. /**
  106. * Callback that is called when the user releases the slider,
  107. * regardless if the value has changed. The current value is passed
  108. * as an argument to the callback handler.
  109. */
  110. onSlidingComplete?: ?(value: number) => void,
  111. /**
  112. * Used to locate this view in UI automation tests.
  113. */
  114. testID?: ?string,
  115. |}>;
  116. /**
  117. * A component used to select a single value from a range of values.
  118. *
  119. * ### Usage
  120. *
  121. * The example below shows how to use `Slider` to change
  122. * a value used by `Text`. The value is stored using
  123. * the state of the root component (`App`). The same component
  124. * subscribes to the `onValueChange` of `Slider` and changes
  125. * the value using `setState`.
  126. *
  127. *```
  128. * import React from 'react';
  129. * import { StyleSheet, Text, View, Slider } from 'react-native';
  130. *
  131. * export default class App extends React.Component {
  132. * constructor(props) {
  133. * super(props);
  134. * this.state = {
  135. * value: 50
  136. * }
  137. * }
  138. *
  139. * change(value) {
  140. * this.setState(() => {
  141. * return {
  142. * value: parseFloat(value)
  143. * };
  144. * });
  145. * }
  146. *
  147. * render() {
  148. * const {value} = this.state;
  149. * return (
  150. * <View style={styles.container}>
  151. * <Text style={styles.text}>{String(value)}</Text>
  152. * <Slider
  153. * step={1}
  154. * maximumValue={100}
  155. * onValueChange={this.change.bind(this)}
  156. * value={value} />
  157. * </View>
  158. * );
  159. * }
  160. * }
  161. *
  162. * const styles = StyleSheet.create({
  163. * container: {
  164. * flex: 1,
  165. * flexDirection: 'column',
  166. * justifyContent: 'center'
  167. * },
  168. * text: {
  169. * fontSize: 50,
  170. * textAlign: 'center'
  171. * }
  172. * });
  173. *```
  174. *
  175. */
  176. const Slider = (
  177. props: Props,
  178. forwardedRef?: ?React.Ref<typeof SliderNativeComponent>,
  179. ) => {
  180. const style = StyleSheet.compose(
  181. styles.slider,
  182. props.style,
  183. );
  184. const {
  185. disabled = false,
  186. value = 0.5,
  187. minimumValue = 0,
  188. maximumValue = 1,
  189. step = 0,
  190. onValueChange,
  191. onSlidingComplete,
  192. ...localProps
  193. } = props;
  194. const onValueChangeEvent = onValueChange
  195. ? (event: Event) => {
  196. let userEvent = true;
  197. if (Platform.OS === 'android') {
  198. // On Android there's a special flag telling us the user is
  199. // dragging the slider.
  200. userEvent =
  201. event.nativeEvent.fromUser != null && event.nativeEvent.fromUser;
  202. }
  203. userEvent && onValueChange(event.nativeEvent.value);
  204. }
  205. : null;
  206. const onChangeEvent = onValueChangeEvent;
  207. const onSlidingCompleteEvent = onSlidingComplete
  208. ? (event: Event) => {
  209. onSlidingComplete(event.nativeEvent.value);
  210. }
  211. : null;
  212. return (
  213. <SliderNativeComponent
  214. {...localProps}
  215. // TODO: Reconcile these across the two platforms.
  216. enabled={!disabled}
  217. disabled={disabled}
  218. maximumValue={maximumValue}
  219. minimumValue={minimumValue}
  220. onChange={onChangeEvent}
  221. onResponderTerminationRequest={() => false}
  222. onSlidingComplete={onSlidingCompleteEvent}
  223. onStartShouldSetResponder={() => true}
  224. onValueChange={onValueChangeEvent}
  225. ref={forwardedRef}
  226. step={step}
  227. style={style}
  228. value={value}
  229. />
  230. );
  231. };
  232. const SliderWithRef: React.AbstractComponent<
  233. Props,
  234. React.ElementRef<typeof SliderNativeComponent>,
  235. > = React.forwardRef(Slider);
  236. let styles;
  237. if (Platform.OS === 'ios') {
  238. styles = StyleSheet.create({
  239. slider: {
  240. height: 40,
  241. },
  242. });
  243. } else {
  244. styles = StyleSheet.create({
  245. slider: {},
  246. });
  247. }
  248. module.exports = SliderWithRef;