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',