nsm 1 tahun lalu
melakukan
39194c500d

+ 43 - 0
.eslintrc

@@ -0,0 +1,43 @@
+{
+    "parser": "babel-eslint",
+    "env": {
+        "es6": true
+    },
+    "parserOptions": {
+        "ecmaVersion": 6,
+        "sourceType": "module",
+        "ecmaFeatures": {
+            "jsx": true
+        }
+    },
+    "plugins": [
+        "react"
+    ],
+    "globals": {
+        "require": false,
+        "module": false,
+        "setInterval": false,
+        "clearInterval": false,
+        "setTimeout": false,
+        "clearTimeout": false,
+        "console": false,
+        "fetch": false,
+        "navigator": false,
+        "await": false,
+        "__DEV__": false,
+        "navigation": false
+    },
+    "extends": [
+        "eslint:recommended",
+        "plugin:react/recommended"
+    ],
+    "rules": {
+        // overrides
+        "react/prop-types": 2,
+        "indent": ["error", 4, "SwitchCase": 1],
+        "react/jsx-indent": ["error", 4],
+        "no-trailing-spaces": 2,
+        "no-console": 0,
+        "comma-dangle": ["error", "never"]
+    }
+}

+ 59 - 0
.gitignore

@@ -0,0 +1,59 @@
+# OSX
+#
+.DS_Store
+.vscode
+
+# Xcode
+#
+build/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata
+*.xccheckout
+*.moved-aside
+DerivedData
+*.hmap
+*.ipa
+*.xcuserstate
+project.xcworkspace
+ios/Pods
+Daphne.xcworkspace/
+
+# Android/IntelliJ
+#
+build/
+.idea
+.gradle
+local.properties
+*.iml
+
+# node.js
+#
+node_modules/
+npm-debug.log
+yarn-error.log
+
+# BUCK
+buck-out/
+\.buckd/
+*.keystore
+
+# fastlane
+#
+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
+# screenshots whenever they are needed.
+# For more information about the recommended setup visit:
+# https://docs.fastlane.tools/best-practices/source-control/
+
+*/fastlane/report.xml
+*/fastlane/Preview.html
+*/fastlane/screenshots
+
+.tern-port
+*.mobileprovision

+ 17 - 0
BAPicker.js

@@ -0,0 +1,17 @@
+import {
+    Platform
+} from 'react-native';
+
+import BAPickerIOS from './BAPickerIOS';
+import BAPickerAndroid from './BAPickerAndroid';
+
+
+let BAPicker;
+
+if(Platform.OS == 'ios') {
+    BAPicker = BAPickerIOS;
+} else {
+    BAPicker = BAPickerAndroid;
+}
+
+export default BAPicker;

+ 55 - 0
BAPickerAndroid.js

@@ -0,0 +1,55 @@
+import React, { Component } from 'react';
+import {
+    Platform,
+    requireNativeComponent,
+    View,
+    ViewPropTypes
+} from 'react-native';
+
+import PropTypes from 'prop-types';
+
+
+let iface = {
+    name: 'PickerView',
+    propTypes: {
+        ...ViewPropTypes,
+        data: PropTypes.array,
+        selectedIndex: PropTypes.number,
+        onValueChange: PropTypes.func,
+        itemStyle: PropTypes.object
+    }
+}
+
+let Picker = Platform.OS == 'ios' ? View : requireNativeComponent('AndroidPicker', iface);
+
+export default class BAPickerAndroid extends Component {
+
+    static propTypes = {
+        ...ViewPropTypes,
+        data: PropTypes.array,
+        selectedIndex: PropTypes.number,
+        onValueChange: PropTypes.func,
+        itemStyle: PropTypes.object
+    }
+
+    render() {
+        data = {
+            list: this.props.data,
+            selectedIndex: this.props.selectedIndex
+        }
+        return (
+            <Picker
+                {...this.props}
+                dataAndSelectedIndex={data}
+                onValueChange={(e) => this._onValueChange(e)}
+            />
+        );
+    }
+
+    _onValueChange(event) {
+        if (!this.props.onValueChange)
+            return;
+        this.props.onValueChange(event.nativeEvent);
+    }
+}
+

+ 41 - 0
BAPickerIOS.js

@@ -0,0 +1,41 @@
+import React, {Component} from 'react';
+
+import {
+    PickerIOS
+} from 'react-native';
+
+import PropTypes from 'prop-types';
+
+export default class BAPickerIOS extends Component {
+    render() {
+        const {
+            data,
+            style,
+            itemStyle = null,
+            selectedIndex,
+            onValueChange=() =>{}
+        } = this.props;
+        return (
+            <PickerIOS
+                style={[{flex: 1}, style]}
+                selectedValue={selectedIndex}
+                itemStyle={itemStyle}
+                onValueChange={(index) => onValueChange({index: index, item: data[index]})}>
+                {data.map((val, index) => (
+                    <PickerIOS.Item
+                        key={val + '-' + index}
+                        value={index}
+                        label={val} />
+                ))}
+            </PickerIOS>
+        )
+    }
+}
+
+BAPickerIOS.propTypes = {
+    data: PropTypes.array,
+    style: PropTypes.any,
+    selectedIndex: PropTypes.number,
+    itemStyle: PropTypes.any,
+    onValueChange: PropTypes.func
+};

+ 131 - 0
BAPickerModal.js

@@ -0,0 +1,131 @@
+
+import React, {Component} from 'react';
+
+import {
+    Modal,
+    Text,
+    TouchableOpacity,
+    View,
+    StyleSheet
+} from 'react-native';
+
+import BAPicker from './BAPicker';
+import PropTypes from 'prop-types';
+
+class BAButton extends Component {
+    render() {
+        const {
+            style,
+            title,
+            onPress
+        } = this.props;
+        return (
+            <TouchableOpacity style={[styles.btnStyle, style]} onPress={() => onPress()}>
+                <Text style={{color: 'white'}}>{title}</Text>
+            </TouchableOpacity>
+        )
+    }
+}
+
+BAButton.propTypes = {
+    style: PropTypes.any,
+    title: PropTypes.string,
+    onPress: PropTypes.func
+};
+
+export default class BAPickerModal extends Component {
+
+    constructor(props) {
+        super(props);
+        this.state = {
+            selectedIndex: this.props.selectedIndex
+        }
+    }
+
+    render() {
+        const {
+            modalVisible,
+            onClosePress,
+            list,
+            onConfirm,
+            itemStyle,
+            topBarStyle,
+            cancelTitle=null,
+            confirmTitle=null,
+            title = ""
+        } = this.props;
+
+        return (
+            <Modal
+                animationType="none"
+                transparent={true}
+                visible={modalVisible}
+                onRequestClose={() => { onClosePress() }}>
+                <View style={styles.modalContainer}>
+                    <View style={[styles.topContainer, topBarStyle]}>
+                        <BAButton title={cancelTitle? cancelTitle: "取消"} onPress={() => onClosePress()} />
+                        <View style={{flex: 1, justifyContent: 'center', alignItems:'center'}}>
+                            <Text style={{color: 'white', fontSize: 16}}>{title?title: ""}</Text>
+                        </View>
+                        <BAButton title={confirmTitle?confirmTitle: "确定"} onPress={() => onConfirm({index: this.state.selectedIndex, item: list[this.state.selectedIndex]})} />
+                    </View>
+
+                    <View style={styles.pickerStyle}>
+                    <BAPicker
+                        style={styles.pickerStyle}
+                        itemStyle={itemStyle}
+                        data={list}
+                        selectedIndex={this.state.selectedIndex}
+                        onValueChange={(resultObj) => { this.setState({selectedIndex: resultObj.index}); }}/>
+                    </View>
+                    
+                </View>
+            </Modal>
+        )
+    }
+
+}
+
+BAPickerModal.propTypes = {
+    modalVisible: PropTypes.bool,
+    onClosePress: PropTypes.func,
+    list: PropTypes.array,
+    selectedIndex: PropTypes.number,
+    onConfirm: PropTypes.func,
+    itemStyle: PropTypes.object,
+    topBarStyle: PropTypes.object,
+    cancelTitle: PropTypes.string,
+    confirmTitle: PropTypes.string,
+    title: PropTypes.string
+};
+
+const styles = StyleSheet.create({
+    modalContainer: {
+        width: '100%',
+        height: '100%',
+        justifyContent: 'flex-end',
+        backgroundColor: '#00000033'
+    },
+
+    topContainer: {
+        width: '100%',
+        height: 44,
+        flexDirection: 'row',
+        backgroundColor: '#58A8F5'
+    },
+
+    pickerStyle: {
+        flex: 0,
+        width: '100%',
+        height: 200,
+        backgroundColor: 'white',
+        overflow: "hidden"
+    },
+
+    btnStyle: {
+        width: 60,
+        height: '100%',
+        justifyContent: 'center',
+        alignItems: 'center'
+    }
+});

+ 0 - 0
README.md


+ 65 - 0
android/BUCK

@@ -0,0 +1,65 @@
+# To learn about Buck see [Docs](https://buckbuild.com/).
+# To run your application with Buck:
+# - install Buck
+# - `npm start` - to start the packager
+# - `cd android`
+# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
+# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
+# - `buck install -r android/app` - compile, install and run application
+#
+
+lib_deps = []
+
+for jarfile in glob(['libs/*.jar']):
+  name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')]
+  lib_deps.append(':' + name)
+  prebuilt_jar(
+    name = name,
+    binary_jar = jarfile,
+  )
+
+for aarfile in glob(['libs/*.aar']):
+  name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')]
+  lib_deps.append(':' + name)
+  android_prebuilt_aar(
+    name = name,
+    aar = aarfile,
+  )
+
+android_library(
+    name = "all-libs",
+    exported_deps = lib_deps,
+)
+
+android_library(
+    name = "app-code",
+    srcs = glob([
+        "src/main/java/**/*.java",
+    ]),
+    deps = [
+        ":all-libs",
+        ":build_config",
+        ":res",
+    ],
+)
+
+android_build_config(
+    name = "build_config",
+    package = "com.picker",
+)
+
+android_resource(
+    name = "res",
+    package = "com.picker",
+    res = "src/main/res",
+)
+
+android_binary(
+    name = "app",
+    keystore = "//android/keystores:debug",
+    manifest = "src/main/AndroidManifest.xml",
+    package_type = "debug",
+    deps = [
+        ":app-code",
+    ],
+)

+ 18 - 0
android/build.gradle

@@ -0,0 +1,18 @@
+apply plugin: "com.android.library"
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion "23.0.1"
+
+    defaultConfig {
+        minSdkVersion 16
+        targetSdkVersion 22
+    }
+
+}
+
+dependencies {
+    implementation fileTree(dir: "libs", include: ["*.jar"])
+    implementation "com.android.support:appcompat-v7:23.0.1"
+    implementation "com.facebook.react:react-native:+"  // From node_modules
+}

+ 70 - 0
android/proguard-rules.pro

@@ -0,0 +1,70 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Disabling obfuscation is useful if you collect stack traces from production crashes
+# (unless you are using a system that supports de-obfuscate the stack traces).
+-dontobfuscate
+
+# React Native
+
+# Keep our interfaces so they can be used by other ProGuard rules.
+# See http://sourceforge.net/p/proguard/bugs/466/
+-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
+-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
+-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
+
+# Do not strip any method/class that is annotated with @DoNotStrip
+-keep @com.facebook.proguard.annotations.DoNotStrip class *
+-keep @com.facebook.common.internal.DoNotStrip class *
+-keepclassmembers class * {
+    @com.facebook.proguard.annotations.DoNotStrip *;
+    @com.facebook.common.internal.DoNotStrip *;
+}
+
+-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
+  void set*(***);
+  *** get*();
+}
+
+-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
+-keep class * extends com.facebook.react.bridge.NativeModule { *; }
+-keepclassmembers,includedescriptorclasses class * { native <methods>; }
+-keepclassmembers class *  { @com.facebook.react.uimanager.UIProp <fields>; }
+-keepclassmembers class *  { @com.facebook.react.uimanager.annotations.ReactProp <methods>; }
+-keepclassmembers class *  { @com.facebook.react.uimanager.annotations.ReactPropGroup <methods>; }
+
+-dontwarn com.facebook.react.**
+
+# TextLayoutBuilder uses a non-public Android constructor within StaticLayout.
+# See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details.
+-dontwarn android.text.StaticLayout
+
+# okhttp
+
+-keepattributes Signature
+-keepattributes *Annotation*
+-keep class okhttp3.** { *; }
+-keep interface okhttp3.** { *; }
+-dontwarn okhttp3.**
+
+# okio
+
+-keep class sun.misc.Unsafe { *; }
+-dontwarn java.nio.file.*
+-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
+-dontwarn okio.**

+ 8 - 0
android/src/main/AndroidManifest.xml

@@ -0,0 +1,8 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.picker">
+
+    <application>
+
+    </application>
+
+</manifest>

+ 144 - 0
android/src/main/java/com/picker/NumberPickerManager.java

@@ -0,0 +1,144 @@
+package com.picker;
+
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.DisplayMetrics;
+
+import android.view.View;
+import android.widget.EditText;
+import android.widget.NumberPicker;
+
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.common.MapBuilder;
+import com.facebook.react.uimanager.SimpleViewManager;
+import com.facebook.react.uimanager.ThemedReactContext;
+import com.facebook.react.uimanager.annotations.ReactProp;
+import com.facebook.react.uimanager.events.RCTEventEmitter;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by haoxinlei on 2018/3/27.
+ */
+
+public class NumberPickerManager extends SimpleViewManager<NumberPicker> {
+
+    private ThemedReactContext context;
+    private final String ONVALUECHANGE = "onValueChange";
+
+    @Override
+    public String getName() {
+        return "AndroidPicker";
+    }
+
+    @Override
+    protected NumberPicker createViewInstance(ThemedReactContext reactContext) {
+        NumberPicker numberPicker = new NumberPicker(reactContext);
+        context = reactContext;
+        return numberPicker;
+    }
+
+    @ReactProp(name = "data")
+    public void setValues(NumberPicker numberPicker, ReadableArray array) {
+        String[] values = new String[array.size()];
+        List<String> data = new ArrayList<>();
+        for (int i = 0; i < array.size(); i++) {
+            values[i] = array.getString(i);
+            data.add(array.getString(i));
+        }
+        numberPicker.setMaxValue(data.size()-1);
+        numberPicker.setMinValue(0);
+        numberPicker.setDisplayedValues(values);
+        numberPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
+        numberPicker.setWrapSelectorWheel(false);//禁止循环滚动
+
+        try {
+            //设置分割线高度
+            Field dividerHeight = numberPicker.getClass().getDeclaredField("mSelectionDividerHeight");
+            dividerHeight.setAccessible(true);
+            dividerHeight.set(numberPicker,1);
+        } catch (Exception e){
+            e.printStackTrace();
+        }
+        numberPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+            @Override
+            public void onValueChange(NumberPicker numberPicker, int i, int i1) {
+                WritableMap map = Arguments.createMap();
+                map.putString("item",numberPicker.getDisplayedValues()[i1]);
+                map.putInt("index",i1);
+                context.getJSModule(RCTEventEmitter.class)
+                        .receiveEvent(numberPicker.getId(),"onValueChange",map);
+            }
+        });
+        numberPicker.invalidate();
+    }
+
+    @ReactProp(name = "dataAndSelectedIndex")
+    public void setDataAndSelectedIndex(NumberPicker numberPicker, ReadableMap map) {
+        if(map.hasKey("list")) {
+            this.setValues(numberPicker, map.getArray("list"));
+        }
+        if(map.hasKey("selectedIndex")) {
+            this.setDefaultValue(numberPicker, map.getInt("selectedIndex"));
+        }
+    }
+
+    @ReactProp(name = "itemStyle")
+    public void setItemStyle(NumberPicker numberPicker, ReadableMap map) {
+        float fontSize = 28;
+        String textColor = "#000000";
+        if (map.hasKey("fontSize"))
+            fontSize = map.getInt("fontSize") * getDensity();
+        if (map.hasKey("color"))
+            textColor = map.getString("color");
+        for (int i = 0; i < numberPicker.getChildCount(); i++) {
+            View view = numberPicker.getChildAt(i);
+            if (view instanceof EditText) {
+                try {
+                    Field selectorWheelPaintField = numberPicker.getClass()
+                            .getDeclaredField("mSelectorWheelPaint");
+                    selectorWheelPaintField.setAccessible(true);
+                    ((Paint) selectorWheelPaintField.get(numberPicker)).setColor(Color.parseColor(textColor));
+                    ((Paint) selectorWheelPaintField.get(numberPicker)).setTextSize(fontSize);
+                    ((EditText) view).setTextColor(Color.parseColor(textColor));
+                    ((EditText) view).setTextSize(fontSize/getDensity());
+                    numberPicker.invalidate();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        numberPicker.invalidate();
+    }
+
+    public float getDensity(){
+        DisplayMetrics dm = new DisplayMetrics();
+        context.getCurrentActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
+        return dm.density;
+    }
+
+    @ReactProp(name = "selectedIndex")
+    public void setDefaultValue(NumberPicker numberPicker, int i) {
+        numberPicker.setValue(i);
+    }
+
+
+    @Override
+    protected void addEventEmitters(ThemedReactContext reactContext, NumberPicker view) {
+
+    }
+
+    @Override
+    public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
+        return MapBuilder.<String, Object>builder()
+                .put(ONVALUECHANGE, MapBuilder.of("registrationName", ONVALUECHANGE))
+                .build();
+    }
+
+}

+ 38 - 0
android/src/main/java/com/picker/NumberPickerMoudle.java

@@ -0,0 +1,38 @@
+package com.picker;
+import android.widget.NumberPicker;
+
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.Callback;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
+import com.facebook.react.bridge.ReactMethod;
+import com.facebook.react.bridge.WritableMap;
+
+/**
+ * Created by haoxinlei on 2018/3/27.
+ */
+
+public class NumberPickerMoudle extends ReactContextBaseJavaModule {
+
+    private ReactApplicationContext reactContext;
+
+    public NumberPickerMoudle(ReactApplicationContext reactContext) {
+        super(reactContext);
+        this.reactContext = reactContext;
+    }
+
+    @Override
+    public String getName() {
+        return "AndroidModule";
+    }
+
+
+    @ReactMethod
+    public void getCurrentValue(int reactTag, Callback callback){
+        NumberPicker myPicker = (NumberPicker) reactContext.getCurrentActivity().findViewById(reactTag);
+        WritableMap map = Arguments.createMap();
+        map.putString("item",myPicker.getDisplayedValues()[myPicker.getValue()]);
+        map.putInt("index",myPicker.getValue());
+        callback.invoke(map);
+    }
+}

+ 29 - 0
android/src/main/java/com/picker/NumberPickerPackage.java

@@ -0,0 +1,29 @@
+package com.picker;
+
+import com.facebook.react.ReactPackage;
+import com.facebook.react.bridge.NativeModule;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.uimanager.ViewManager;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Created by haoxinlei on 2018/3/27.
+ */
+
+public class NumberPickerPackage implements ReactPackage {
+    @Override
+    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
+        return Arrays.<NativeModule>asList(
+                new NumberPickerMoudle(reactContext)
+        );
+    }
+
+    @Override
+    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
+        return Arrays.<ViewManager>asList(
+                new NumberPickerManager()
+        );
+    }
+}

+ 3 - 0
android/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">picker</string>
+</resources>

+ 8 - 0
android/src/main/res/values/styles.xml

@@ -0,0 +1,8 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
+        <!-- Customize your theme here. -->
+    </style>
+
+</resources>

+ 42 - 0
index.d.ts

@@ -0,0 +1,42 @@
+import React, {Component} from 'react';
+import {
+    ViewStyle
+} from 'react-native';
+
+interface Result {
+    item: string;
+    index: number;
+}
+
+interface BAPickerModalProps {
+    modalVisible: boolean;
+    onClosePress: ()=>void;
+    list: Array<string>;
+    selectedIndex: number;
+    onConfirm: (result: Result) => void;
+    itemStyle?: ViewStyle;
+    topBarStyle?: ViewStyle;
+    cancelTitle?: string;
+    confirmTitle?: string;
+}
+
+ export class BAPickerModal extends Component<BAPickerModalProps> {
+    
+ }
+
+ export interface PickerItem {
+     index: number;
+     item: string;
+ }
+
+ interface BAPickerProps {
+    data: Array<string>
+    style?: ViewStyle;
+    selectedIndex: number;
+    itemStyle?: ViewStyle;
+    onValueChange: (item: PickerItem)=>void
+ }
+
+ export class BAPicker extends Component<BAPickerProps> {
+    
+ }

+ 6 - 0
index.js

@@ -0,0 +1,6 @@
+
+
+import BAPicker from './BAPicker';
+import BAPickerModal from './BAPickerModal';
+
+export {BAPicker, BAPickerModal};

+ 11 - 0
package.json

@@ -0,0 +1,11 @@
+{
+  "name": "react-native-ba-picker",
+  "version": "0.0.1",
+  "description": "A aeneral picker made by brilliantaero.com",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "brilliantaero",
+  "license": "MIT"
+}