diff --git a/Examples/UIExplorer/SliderExample.js b/Examples/UIExplorer/SliderExample.js
new file mode 100644
index 000000000..edf44d980
--- /dev/null
+++ b/Examples/UIExplorer/SliderExample.js
@@ -0,0 +1,169 @@
+/**
+ * The examples provided by Facebook are for non-commercial testing and
+ * evaluation purposes only.
+ *
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * @flow
+ */
+'use strict';
+
+var React = require('react-native');
+var {
+ Slider,
+ Text,
+ StyleSheet,
+ View,
+} = React;
+
+var SliderExample = React.createClass({
+ getDefaultProps() {
+ return {
+ value: 0,
+ }
+ },
+
+ getInitialState() {
+ return {
+ value: this.props.value,
+ };
+ },
+
+ render() {
+ return (
+
+
+ {this.state.value && +this.state.value.toFixed(3)}
+
+ this.setState({value: value})} />
+
+ );
+ }
+});
+
+var SlidingCompleteExample = React.createClass({
+ getInitialState() {
+ return {
+ slideCompletionValue: 0,
+ slideCompletionCount: 0,
+ };
+ },
+
+ render() {
+ return (
+
+ this.setState({
+ slideCompletionValue: value,
+ slideCompletionCount: this.state.slideCompletionCount + 1})} />
+
+ Completions: {this.state.slideCompletionCount} Value: {this.state.slideCompletionValue}
+
+
+ );
+ }
+});
+
+var styles = StyleSheet.create({
+ slider: {
+ height: 10,
+ margin: 10,
+ },
+ text: {
+ fontSize: 14,
+ textAlign: 'center',
+ fontWeight: '500',
+ margin: 10,
+ },
+});
+
+exports.title = '';
+exports.displayName = 'SliderExample';
+exports.description = 'Slider input for numeric values';
+exports.examples = [
+ {
+ title: 'Default settings',
+ render(): ReactElement {
+ return ;
+ }
+ },
+ {
+ title: 'Initial value: 0.5',
+ render(): ReactElement {
+ return ;
+ }
+ },
+ {
+ title: 'minimumValue: -1, maximumValue: 2',
+ render(): ReactElement {
+ return (
+
+ );
+ }
+ },
+ {
+ title: 'step: 0.25',
+ render(): ReactElement {
+ return ;
+ }
+ },
+ {
+ title: 'onSlidingComplete',
+ render(): ReactElement {
+ return (
+
+ );
+ }
+ },
+ {
+ title: 'Custom min/max track tint color',
+ platform: 'ios',
+ render(): ReactElement {
+ return (
+
+ );
+ }
+ },
+ {
+ title: 'Custom thumb image',
+ platform: 'ios',
+ render(): ReactElement {
+ return ;
+ }
+ },
+ {
+ title: 'Custom track image',
+ platform: 'ios',
+ render(): ReactElement {
+ return ;
+ }
+ },
+ {
+ title: 'Custom min/max track image',
+ platform: 'ios',
+ render(): ReactElement {
+ return (
+
+ );
+ }
+ },
+];
diff --git a/Examples/UIExplorer/UIExplorerList.android.js b/Examples/UIExplorer/UIExplorerList.android.js
index bb5b13a81..97f450338 100644
--- a/Examples/UIExplorer/UIExplorerList.android.js
+++ b/Examples/UIExplorer/UIExplorerList.android.js
@@ -23,6 +23,10 @@ export type UIExplorerExample = {
};
var ComponentExamples: Array = [
+ {
+ key: 'SliderExample',
+ module: require('./SliderExample'),
+ },
{
key: 'ImageExample',
module: require('./ImageExample'),
diff --git a/Examples/UIExplorer/UIExplorerList.ios.js b/Examples/UIExplorer/UIExplorerList.ios.js
index 8d770d1f4..33a1ab76a 100644
--- a/Examples/UIExplorer/UIExplorerList.ios.js
+++ b/Examples/UIExplorer/UIExplorerList.ios.js
@@ -50,8 +50,8 @@ var ComponentExamples: Array = [
module: require('./ListViewPagingExample'),
},
{
- key: 'MapViewExample',
- module: require('./MapViewExample'),
+ key: 'MapViewExample',
+ module: require('./MapViewExample'),
},
{
key: 'ModalExample',
@@ -90,8 +90,8 @@ var ComponentExamples: Array = [
module: require('./SegmentedControlIOSExample'),
},
{
- key: 'SliderIOSExample',
- module: require('./SliderIOSExample'),
+ key: 'SliderExample',
+ module: require('./SliderExample'),
},
{
key: 'StatusBarExample',
diff --git a/Libraries/Components/Slider/Slider.js b/Libraries/Components/Slider/Slider.js
new file mode 100644
index 000000000..3ace87615
--- /dev/null
+++ b/Libraries/Components/Slider/Slider.js
@@ -0,0 +1,191 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule Slider
+ * @flow
+ */
+'use strict';
+
+var Image = require('Image');
+var NativeMethodsMixin = require('NativeMethodsMixin');
+var Platform = require('Platform');
+var PropTypes = require('ReactPropTypes');
+var React = require('React');
+var StyleSheet = require('StyleSheet');
+var View = require('View');
+
+var requireNativeComponent = require('requireNativeComponent');
+
+type Event = Object;
+
+var Slider = React.createClass({
+ mixins: [NativeMethodsMixin],
+
+ propTypes: {
+ ...View.propTypes,
+
+ /**
+ * Used to style and layout the `Slider`. See `StyleSheet.js` and
+ * `ViewStylePropTypes.js` for more info.
+ */
+ style: View.propTypes.style,
+
+ /**
+ * Initial value of the slider. The value should be between minimumValue
+ * and maximumValue, which default to 0 and 1 respectively.
+ * Default value is 0.
+ *
+ * *This is not a controlled component*, you don't need to update the
+ * value during dragging.
+ */
+ value: PropTypes.number,
+
+ /**
+ * Step value of the slider. The value should be
+ * between 0 and (maximumValue - minimumValue).
+ * Default value is 0.
+ */
+ step: PropTypes.number,
+
+ /**
+ * Initial minimum value of the slider. Default value is 0.
+ */
+ minimumValue: PropTypes.number,
+
+ /**
+ * Initial maximum value of the slider. Default value is 1.
+ */
+ maximumValue: PropTypes.number,
+
+ /**
+ * The color used for the track to the left of the button. Overrides the
+ * default blue gradient image.
+ * @platform ios
+ */
+ minimumTrackTintColor: PropTypes.string,
+
+ /**
+ * The color used for the track to the right of the button. Overrides the
+ * default blue gradient image.
+ * @platform ios
+ */
+ maximumTrackTintColor: PropTypes.string,
+
+ /**
+ * If true the user won't be able to move the slider.
+ * Default value is false.
+ */
+ disabled: PropTypes.bool,
+
+ /**
+ * Assigns a single image for the track. Only static images are supported.
+ * The center pixel of the image will be stretched to fill the track.
+ * @platform ios
+ */
+ trackImage: Image.propTypes.source,
+
+ /**
+ * Assigns a minimum track image. Only static images are supported. The
+ * rightmost pixel of the image will be stretched to fill the track.
+ * @platform ios
+ */
+ minimumTrackImage: Image.propTypes.source,
+
+ /**
+ * Assigns a maximum track image. Only static images are supported. The
+ * leftmost pixel of the image will be stretched to fill the track.
+ * @platform ios
+ */
+ maximumTrackImage: Image.propTypes.source,
+
+ /**
+ * Sets an image for the thumb. Only static images are supported.
+ * @platform ios
+ */
+ thumbImage: Image.propTypes.source,
+
+ /**
+ * Callback continuously called while the user is dragging the slider.
+ */
+ onValueChange: PropTypes.func,
+
+ /**
+ * Callback called when the user finishes changing the value (e.g. when
+ * the slider is released).
+ */
+ onSlidingComplete: PropTypes.func,
+
+ /**
+ * Used to locate this view in UI automation tests.
+ */
+ testID: PropTypes.string,
+ },
+
+ getDefaultProps: function() : any {
+ return {
+ disabled: false,
+ value: 0,
+ minimumValue: 0,
+ maximumValue: 1,
+ step: 0
+ };
+ },
+
+ render: function() {
+ let {style, onValueChange, onSlidingComplete, ...props} = this.props;
+ props.style = [styles.slider, style];
+
+ props.onValueChange = onValueChange && ((event: Event) => {
+ let userEvent = true;
+ if (Platform.OS === 'android') {
+ // On Android there's a special flag telling us the user is
+ // dragging the slider.
+ userEvent = event.nativeEvent.fromUser;
+ }
+ onValueChange && userEvent && onValueChange(event.nativeEvent.value);
+ });
+
+ props.onChange = props.onValueChange;
+
+ props.onSlidingComplete = onSlidingComplete && ((event: Event) => {
+ onSlidingComplete && onSlidingComplete(event.nativeEvent.value);
+ });
+
+ return true}
+ onResponderTerminationRequest={() => false}
+ />;
+ }
+});
+
+let styles;
+if (Platform.OS === 'ios') {
+ styles = StyleSheet.create({
+ slider: {
+ height: 40,
+ },
+ });
+} else {
+ styles = StyleSheet.create({
+ slider: {},
+ });
+}
+
+let options = {};
+if (Platform.OS === 'android') {
+ options = {
+ nativeOnly: {
+ enabled: true,
+ }
+ };
+}
+const RCTSlider = requireNativeComponent('RCTSlider', Slider, options);
+
+module.exports = Slider;
diff --git a/Libraries/Components/SliderIOS/SliderIOS.ios.js b/Libraries/Components/SliderIOS/SliderIOS.ios.js
index c78dc4a47..d85ebfddd 100644
--- a/Libraries/Components/SliderIOS/SliderIOS.ios.js
+++ b/Libraries/Components/SliderIOS/SliderIOS.ios.js
@@ -120,6 +120,11 @@ var SliderIOS = React.createClass({
},
render: function() {
+ console.warn(
+ 'SliderIOS is deprecated and will be removed in ' +
+ 'future versions of React Native. Use the cross-platform Slider ' +
+ 'as a drop-in replacement.');
+
let {style, onValueChange, onSlidingComplete, ...props} = this.props;
props.style = [styles.slider, style];
diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js
index 2b5a0bc96..f52968e66 100644
--- a/Libraries/react-native/react-native.js
+++ b/Libraries/react-native/react-native.js
@@ -31,6 +31,7 @@ var ReactNative = {
get ProgressViewIOS() { return require('ProgressViewIOS'); },
get ScrollView() { return require('ScrollView'); },
get SegmentedControlIOS() { return require('SegmentedControlIOS'); },
+ get Slider() { return require('Slider'); },
get SliderIOS() { return require('SliderIOS'); },
get SnapshotViewIOS() { return require('SnapshotViewIOS'); },
get Switch() { return require('Switch'); },
diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK b/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK
index 5b70682fd..c5d4d0c1e 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK
+++ b/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK
@@ -17,6 +17,7 @@ android_library(
react_native_target('java/com/facebook/react/views/progressbar:progressbar'),
react_native_target('java/com/facebook/react/views/recyclerview:recyclerview'),
react_native_target('java/com/facebook/react/views/scroll:scroll'),
+ react_native_target('java/com/facebook/react/views/slider:slider'),
react_native_target('java/com/facebook/react/views/swiperefresh:swiperefresh'),
react_native_target('java/com/facebook/react/views/switchview:switchview'),
react_native_target('java/com/facebook/react/views/text:text'),
diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java
index 2df9f49eb..143fc271f 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java
@@ -47,6 +47,7 @@ import com.facebook.react.views.progressbar.ReactProgressBarViewManager;
import com.facebook.react.views.recyclerview.RecyclerViewBackedScrollViewManager;
import com.facebook.react.views.scroll.ReactHorizontalScrollViewManager;
import com.facebook.react.views.scroll.ReactScrollViewManager;
+import com.facebook.react.views.slider.ReactSliderManager;
import com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager;
import com.facebook.react.views.switchview.ReactSwitchManager;
import com.facebook.react.views.text.ReactRawTextManager;
@@ -109,6 +110,7 @@ public class MainReactPackage implements ReactPackage {
new ReactProgressBarViewManager(),
new ReactRawTextManager(),
new ReactScrollViewManager(),
+ new ReactSliderManager(),
new ReactSwitchManager(),
new FrescoBasedReactTextInlineImageViewManager(),
new ReactTextInputManager(),
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/slider/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/slider/BUCK
new file mode 100644
index 000000000..2b87a27d4
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/slider/BUCK
@@ -0,0 +1,23 @@
+include_defs('//ReactAndroid/DEFS')
+
+android_library(
+ name = 'slider',
+ srcs = glob(['*.java']),
+ deps = [
+ react_native_target('java/com/facebook/react/bridge:bridge'),
+ react_native_target('java/com/facebook/react/common:common'),
+ react_native_target('java/com/facebook/csslayout:csslayout'),
+ react_native_target('java/com/facebook/react/uimanager:uimanager'),
+ react_native_target('java/com/facebook/react/uimanager/annotations:annotations'),
+ react_native_dep('android_res/android/support/v7/appcompat-orig:res-for-react-native'),
+ react_native_dep('third-party/android/support/v7/appcompat-orig:appcompat'),
+ react_native_dep('third-party/java/jsr-305:jsr-305'),
+ ],
+ visibility = [
+ 'PUBLIC',
+ ],
+)
+
+project_config(
+ src_target = ':slider',
+)
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSlider.java b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSlider.java
new file mode 100644
index 000000000..5c6710d2f
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSlider.java
@@ -0,0 +1,111 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package com.facebook.react.views.slider;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.SeekBar;
+
+import javax.annotation.Nullable;
+
+/**
+ * Slider that behaves more like the iOS one, for consistency.
+ *
+ * On iOS, the value is 0..1. Android SeekBar only supports integer values.
+ * For consistency, we pretend in JS that the value is 0..1 but set the
+ * SeekBar value to 0..100.
+ *
+ * Note that the slider is _not_ a controlled component (setValue isn't called
+ * during dragging).
+ */
+public class ReactSlider extends SeekBar {
+
+ /**
+ * If step is 0 (unset) we default to this total number of steps.
+ * Don't use 100 which leads to rounding errors (0.200000000001).
+ */
+ private static int DEFAULT_TOTAL_STEPS = 128;
+
+ /**
+ * We want custom min..max range.
+ * Android only supports 0..max range so we implement this ourselves.
+ */
+ private double mMinValue = 0;
+ private double mMaxValue = 0;
+
+ /**
+ * Value sent from JS (setState).
+ * Doesn't get updated during drag (slider is not a controlled component).
+ */
+ private double mValue = 0;
+
+ /**
+ * If zero it's determined automatically.
+ */
+ private double mStep = 0;
+
+ public ReactSlider(Context context, @Nullable AttributeSet attrs, int style) {
+ super(context, attrs, style);
+ }
+
+ /* package */ void setMaxValue(double max) {
+ mMaxValue = max;
+ updateAll();
+ }
+
+ /* package */ void setMinValue(double min) {
+ mMinValue = min;
+ updateAll();
+ }
+
+ /* package */ void setValue(double value) {
+ mValue = value;
+ updateValue();
+ }
+
+ /* package */ void setStep(double step) {
+ mStep = step;
+ updateAll();
+ }
+
+ /**
+ * Convert SeekBar's native progress value (e.g. 0..100) to a value
+ * passed to JS (e.g. -1.0..2.5).
+ */
+ public double toRealProgress(int seekBarProgress) {
+ if (seekBarProgress == getMax()) {
+ return mMaxValue;
+ }
+ return seekBarProgress * mStep + mMinValue;
+ }
+
+ /**
+ * Update underlying native SeekBar's values.
+ */
+ private void updateAll() {
+ if (mStep == 0) {
+ mStep = (mMaxValue - mMinValue) / (double) DEFAULT_TOTAL_STEPS;
+ }
+ setMax(getTotalSteps());
+ updateValue();
+ }
+
+ /**
+ * Update value only (optimization in case only value is set).
+ */
+ private void updateValue() {
+ setProgress((int) Math.round(
+ (mValue - mMinValue) / (mMaxValue - mMinValue) * getTotalSteps()));
+ }
+
+ private int getTotalSteps() {
+ return (int) Math.ceil((mMaxValue - mMinValue) / mStep);
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderEvent.java
new file mode 100644
index 000000000..20448246d
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderEvent.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package com.facebook.react.views.slider;
+
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.uimanager.events.Event;
+import com.facebook.react.uimanager.events.RCTEventEmitter;
+
+/**
+ * Event emitted by a ReactSliderManager when user changes slider position.
+ */
+public class ReactSliderEvent extends Event {
+
+ public static final String EVENT_NAME = "topChange";
+
+ private final double mValue;
+ private final boolean mFromUser;
+
+ public ReactSliderEvent(int viewId, long timestampMs, double value, boolean fromUser) {
+ super(viewId, timestampMs);
+ mValue = value;
+ mFromUser = fromUser;
+ }
+
+ public double getValue() {
+ return mValue;
+ }
+
+ public boolean isFromUser() {
+ return mFromUser;
+ }
+
+
+ @Override
+ public String getEventName() {
+ return EVENT_NAME;
+ }
+
+ @Override
+ public short getCoalescingKey() {
+ return 0;
+ }
+ @Override
+ public void dispatch(RCTEventEmitter rctEventEmitter) {
+ rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
+ }
+
+ private WritableMap serializeEventData() {
+ WritableMap eventData = Arguments.createMap();
+ eventData.putInt("target", getViewTag());
+ eventData.putDouble("value", getValue());
+ eventData.putBoolean("fromUser", isFromUser());
+ return eventData;
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java
new file mode 100644
index 000000000..7270457da
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java
@@ -0,0 +1,155 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package com.facebook.react.views.slider;
+
+import java.util.Map;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SeekBar;
+
+import com.facebook.csslayout.CSSNode;
+import com.facebook.csslayout.MeasureOutput;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.common.MapBuilder;
+import com.facebook.react.common.SystemClock;
+import com.facebook.react.uimanager.LayoutShadowNode;
+import com.facebook.react.uimanager.SimpleViewManager;
+import com.facebook.react.uimanager.ThemedReactContext;
+import com.facebook.react.uimanager.UIManagerModule;
+import com.facebook.react.uimanager.ViewProps;
+import com.facebook.react.uimanager.annotations.ReactProp;
+
+/**
+ * Manages instances of {@code ReactSlider}.
+ *
+ * Note that the slider is _not_ a controlled component.
+ */
+public class ReactSliderManager extends SimpleViewManager {
+
+ private static final int STYLE = android.R.attr.seekBarStyle;
+
+ private static final String REACT_CLASS = "RCTSlider";
+
+ static class ReactSliderShadowNode extends LayoutShadowNode implements
+ CSSNode.MeasureFunction {
+
+ private int mWidth;
+ private int mHeight;
+ private boolean mMeasured;
+
+ private ReactSliderShadowNode() {
+ setMeasureFunction(this);
+ }
+
+ @Override
+ public void measure(CSSNode node, float width, float height, MeasureOutput measureOutput) {
+ if (!mMeasured) {
+ SeekBar reactSlider = new ReactSlider(getThemedContext(), null, STYLE);
+ final int spec = View.MeasureSpec.makeMeasureSpec(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ View.MeasureSpec.UNSPECIFIED);
+ reactSlider.measure(spec, spec);
+ mWidth = reactSlider.getMeasuredWidth();
+ mHeight = reactSlider.getMeasuredHeight();
+ mMeasured = true;
+ }
+ measureOutput.width = mWidth;
+ measureOutput.height = mHeight;
+ }
+ }
+
+ private static final SeekBar.OnSeekBarChangeListener ON_CHANGE_LISTENER =
+ new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekbar, int progress, boolean fromUser) {
+ ReactContext reactContext = (ReactContext) seekbar.getContext();
+ reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(
+ new ReactSliderEvent(
+ seekbar.getId(),
+ SystemClock.nanoTime(),
+ ((ReactSlider)seekbar).toRealProgress(progress),
+ fromUser));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekbar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekbar) {
+ ReactContext reactContext = (ReactContext) seekbar.getContext();
+ reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(
+ new ReactSlidingCompleteEvent(
+ seekbar.getId(),
+ SystemClock.nanoTime(),
+ ((ReactSlider)seekbar).toRealProgress(seekbar.getProgress())));
+ }
+ };
+
+ @Override
+ public String getName() {
+ return REACT_CLASS;
+ }
+
+ @Override
+ public LayoutShadowNode createShadowNodeInstance() {
+ return new ReactSliderShadowNode();
+ }
+
+ @Override
+ public Class getShadowNodeClass() {
+ return ReactSliderShadowNode.class;
+ }
+
+ @Override
+ protected ReactSlider createViewInstance(ThemedReactContext context) {
+ return new ReactSlider(context, null, STYLE);
+ }
+
+ @ReactProp(name = ViewProps.ENABLED, defaultBoolean = true)
+ public void setEnabled(ReactSlider view, boolean enabled) {
+ view.setEnabled(enabled);
+ }
+
+ @ReactProp(name = "value", defaultDouble = 0d)
+ public void setValue(ReactSlider view, double value) {
+ view.setOnSeekBarChangeListener(null);
+ view.setValue(value);
+ view.setOnSeekBarChangeListener(ON_CHANGE_LISTENER);
+ }
+
+ @ReactProp(name = "minimumValue", defaultDouble = 0d)
+ public void setMinimumValue(ReactSlider view, double value) {
+ view.setMinValue(value);
+ }
+
+ @ReactProp(name = "maximumValue", defaultDouble = 1d)
+ public void setMaximumValue(ReactSlider view, double value) {
+ view.setMaxValue(value);
+ }
+
+ @ReactProp(name = "step", defaultDouble = 0d)
+ public void setStep(ReactSlider view, double value) {
+ view.setStep(value);
+ }
+
+ @Override
+ protected void addEventEmitters(final ThemedReactContext reactContext, final ReactSlider view) {
+ view.setOnSeekBarChangeListener(ON_CHANGE_LISTENER);
+ }
+
+ @Override
+ public Map getExportedCustomDirectEventTypeConstants() {
+ return MapBuilder.of(
+ ReactSlidingCompleteEvent.EVENT_NAME,
+ MapBuilder.of("registrationName", "onSlidingComplete"));
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSlidingCompleteEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSlidingCompleteEvent.java
new file mode 100644
index 000000000..d9a698d75
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSlidingCompleteEvent.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package com.facebook.react.views.slider;
+
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.uimanager.events.Event;
+import com.facebook.react.uimanager.events.RCTEventEmitter;
+
+/**
+ * Event emitted when the user finishes dragging the slider.
+ */
+public class ReactSlidingCompleteEvent extends Event {
+
+ public static final String EVENT_NAME = "topSlidingComplete";
+
+ private final double mValue;
+
+ public ReactSlidingCompleteEvent(int viewId, long timestampMs, double value) {
+ super(viewId, timestampMs);
+ mValue = value;
+ }
+
+ public double getValue() {
+ return mValue;
+ }
+
+ @Override
+ public String getEventName() {
+ return EVENT_NAME;
+ }
+
+ @Override
+ public short getCoalescingKey() {
+ return 0;
+ }
+
+ @Override
+ public boolean canCoalesce() {
+ return false;
+ }
+
+ @Override
+ public void dispatch(RCTEventEmitter rctEventEmitter) {
+ rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
+ }
+
+ private WritableMap serializeEventData() {
+ WritableMap eventData = Arguments.createMap();
+ eventData.putInt("target", getViewTag());
+ eventData.putDouble("value", getValue());
+ return eventData;
+ }
+
+}
diff --git a/website/server/extractDocs.js b/website/server/extractDocs.js
index 355ca38f5..5d3a9fec6 100644
--- a/website/server/extractDocs.js
+++ b/website/server/extractDocs.js
@@ -209,6 +209,7 @@ var components = [
'../Libraries/Components/RefreshControl/RefreshControl.js',
'../Libraries/Components/ScrollView/ScrollView.js',
'../Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.ios.js',
+ '../Libraries/Components/SliderIOS/Slider.js',
'../Libraries/Components/SliderIOS/SliderIOS.ios.js',
'../Libraries/Components/StatusBar/StatusBar.js',
'../Libraries/Components/Switch/Switch.js',