Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @react-native-community/[picker|slider] #8451

Merged
merged 7 commits into from May 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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;
}
}