Skip to content

Commit

Permalink
Add @react-native-community/[picker|slider] (#8451)
Browse files Browse the repository at this point in the history
  • Loading branch information
brentvatne committed May 27, 2020
1 parent 6993d56 commit 367ce55
Show file tree
Hide file tree
Showing 38 changed files with 1,824 additions and 560 deletions.
Expand Up @@ -49,6 +49,8 @@
import versioned.host.exp.exponent.modules.api.cognito.RNAWSCognitoModule;
import versioned.host.exp.exponent.modules.api.components.datetimepicker.RNDateTimePickerPackage;
import versioned.host.exp.exponent.modules.api.components.maskedview.RNCMaskedViewPackage;
import versioned.host.exp.exponent.modules.api.components.picker.RNCPickerPackage;
import versioned.host.exp.exponent.modules.api.components.slider.ReactSliderPackage;
import versioned.host.exp.exponent.modules.api.components.gesturehandler.react.RNGestureHandlerModule;
import versioned.host.exp.exponent.modules.api.components.gesturehandler.react.RNGestureHandlerPackage;
import versioned.host.exp.exponent.modules.api.components.lottie.LottiePackage;
Expand Down Expand Up @@ -251,6 +253,8 @@ public List<ViewManager> createViewManagers(ReactApplicationContext reactContext
new RNSharedElementPackage(),
new RNDateTimePickerPackage(),
new RNCMaskedViewPackage(),
new RNCPickerPackage(),
new ReactSliderPackage(),
new RNCViewPagerPackage(),
new ExpoAppearancePackage()
));
Expand Down
@@ -0,0 +1,40 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package versioned.host.exp.exponent.modules.api.components.picker;

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;

public class PickerItemSelectEvent extends Event<PickerItemSelectEvent> {
public static final String EVENT_NAME = "topSelect";

private final int mPosition;

public PickerItemSelectEvent(int id, int position) {
super(id);
mPosition = position;
}

@Override
public String getEventName() {
return EVENT_NAME;
}

@Override
public void dispatch(RCTEventEmitter rctEventEmitter) {
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
}

private WritableMap serializeEventData() {
WritableMap eventData = Arguments.createMap();
eventData.putInt("position", mPosition);
return eventData;
}
}
@@ -0,0 +1,32 @@

package versioned.host.exp.exponent.modules.api.components.picker;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.bridge.JavaScriptModule;
public class RNCPickerPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}

// Deprecated from RN 0.47
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
List<ViewManager> list = new ArrayList<>();
list.add(new ReactDialogPickerManager());
list.add(new ReactDropdownPickerManager());
return list;
}
}
@@ -0,0 +1,33 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package versioned.host.exp.exponent.modules.api.components.picker;

import android.widget.Spinner;

import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.ThemedReactContext;


/**
* {@link ReactPickerManager} for {@link ReactPicker} with {@link Spinner#MODE_DIALOG}.
*/
@ReactModule(name = ReactDialogPickerManager.REACT_CLASS)
public class ReactDialogPickerManager extends ReactPickerManager {

public static final String REACT_CLASS = "AndroidDialogPicker";

@Override
public String getName() {
return REACT_CLASS;
}

@Override
protected ReactPicker createViewInstance(ThemedReactContext reactContext) {
return new ReactPicker(reactContext, Spinner.MODE_DIALOG);
}
}
@@ -0,0 +1,32 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package versioned.host.exp.exponent.modules.api.components.picker;

import android.widget.Spinner;

import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.ThemedReactContext;

/**
* {@link ReactPickerManager} for {@link ReactPicker} with {@link Spinner#MODE_DROPDOWN}.
*/
@ReactModule(name = ReactDropdownPickerManager.REACT_CLASS)
public class ReactDropdownPickerManager extends ReactPickerManager {

public static final String REACT_CLASS = "AndroidDropdownPicker";

@Override
public String getName() {
return REACT_CLASS;
}

@Override
protected ReactPicker createViewInstance(ThemedReactContext reactContext) {
return new ReactPicker(reactContext, Spinner.MODE_DROPDOWN);
}
}
@@ -0,0 +1,155 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package versioned.host.exp.exponent.modules.api.components.picker;

import android.content.Context;
import androidx.appcompat.widget.AppCompatSpinner;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Spinner;

import com.facebook.react.common.annotations.VisibleForTesting;

import javax.annotation.Nullable;

public class ReactPicker extends AppCompatSpinner {

private int mMode = Spinner.MODE_DIALOG;
private @Nullable Integer mPrimaryColor;
private @Nullable OnSelectListener mOnSelectListener;
private @Nullable Integer mStagedSelection;

private final OnItemSelectedListener mItemSelectedListener = new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (mOnSelectListener != null) {
mOnSelectListener.onItemSelected(position);
}
}

@Override
public void onNothingSelected(AdapterView<?> parent) {
if (mOnSelectListener != null) {
mOnSelectListener.onItemSelected(-1);
}
}
};

/**
* Listener interface for ReactPicker events.
*/
public interface OnSelectListener {
void onItemSelected(int position);
}

public ReactPicker(Context context) {
super(context);
}

public ReactPicker(Context context, int mode) {
super(context, mode);
mMode = mode;
}

public ReactPicker(Context context, AttributeSet attrs) {
super(context, attrs);
}

public ReactPicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

public ReactPicker(Context context, AttributeSet attrs, int defStyle, int mode) {
super(context, attrs, defStyle, mode);
mMode = mode;
}

private final Runnable measureAndLayout = new Runnable() {
@Override
public void run() {
measure(
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
}
};

@Override
public void requestLayout() {
super.requestLayout();

// The spinner relies on a measure + layout pass happening after it calls requestLayout().
// Without this, the widget never actually changes the selection and doesn't call the
// appropriate listeners. Since we override onLayout in our ViewGroups, a layout pass never
// happens after a call to requestLayout, so we simulate one here.
post(measureAndLayout);
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);

// onItemSelected gets fired immediately after layout because checkSelectionChanged() in
// AdapterView updates the selection position from the default INVALID_POSITION.
// To match iOS behavior, which no onItemSelected during initial layout.
// We setup the listener after layout.
if (getOnItemSelectedListener() == null)
setOnItemSelectedListener(mItemSelectedListener);
}

public void setOnSelectListener(@Nullable OnSelectListener onSelectListener) {
mOnSelectListener = onSelectListener;
}

@Nullable public OnSelectListener getOnSelectListener() {
return mOnSelectListener;
}

/**
* Will cache "selection" value locally and set it only once {@link #updateStagedSelection} is
* called
*/
public void setStagedSelection(int selection) {
mStagedSelection = selection;
}

public void updateStagedSelection() {
if (mStagedSelection != null) {
setSelectionWithSuppressEvent(mStagedSelection);
mStagedSelection = null;
}
}

/**
* Set the selection while suppressing the follow-up {@link OnSelectListener#onItemSelected(int)}
* event. This is used so we don't get an event when changing the selection ourselves.
*
* @param position the position of the selected item
*/
private void setSelectionWithSuppressEvent(int position) {
if (position != getSelectedItemPosition()) {
setOnItemSelectedListener(null);
setSelection(position, false);
setOnItemSelectedListener(mItemSelectedListener);
}
}

public @Nullable Integer getPrimaryColor() {
return mPrimaryColor;
}

public void setPrimaryColor(@Nullable Integer primaryColor) {
mPrimaryColor = primaryColor;
}

@VisibleForTesting
public int getMode() {
return mMode;
}
}

0 comments on commit 367ce55

Please sign in to comment.