Browse Source

feat: support react native's new architecture (#678)

master
Henning Hall 2 years ago
parent
commit
9c448b2cba
39 changed files with 3700 additions and 5478 deletions
  1. +0
    -0
      .github/workflows/e2e-tests.yml
  2. +0
    -124
      .github/workflows/test-rn069.yml
  3. +7
    -6
      .github/workflows/unit-tests.yml
  4. +6
    -2
      README.md
  5. +45
    -1
      android/build.gradle
  6. +0
    -0
      android/gradlew
  7. +0
    -111
      android/src/main/java/com/henninghall/date_picker/DatePickerManager.java
  8. +82
    -0
      android/src/main/java/com/henninghall/date_picker/DatePickerManagerImpl.java
  9. +127
    -0
      android/src/main/java/com/henninghall/date_picker/DatePickerModuleImpl.java
  10. +47
    -8
      android/src/main/java/com/henninghall/date_picker/DatePickerPackage.java
  11. +22
    -2
      android/src/main/java/com/henninghall/date_picker/Emitter.java
  12. +4
    -0
      android/src/main/java/com/henninghall/date_picker/PickerView.java
  13. +9
    -2
      android/src/main/java/com/henninghall/date_picker/State.java
  14. +12
    -0
      android/src/main/java/com/henninghall/date_picker/props/IdProp.java
  15. +3
    -3
      android/src/main/java/com/henninghall/date_picker/props/TimezoneOffsetInMinutesProp.java
  16. +1
    -1
      android/src/main/java/com/henninghall/date_picker/ui/WheelChangeListenerImpl.java
  17. +65
    -0
      android/src/newarch/java/com/henninghall/date_picker/DatePickerManager.java
  18. +27
    -7
      android/src/newarch/java/com/henninghall/date_picker/DatePickerModule.java
  19. +86
    -0
      android/src/oldarch/java/com/henninghall/date_picker/DatePickerManager.java
  20. +37
    -0
      android/src/oldarch/java/com/henninghall/date_picker/DatePickerModule.java
  21. +3
    -0
      examples/Rn072/android/app/build.gradle
  22. +60
    -0
      examples/Rn072/android/app/src/test/java/ShortestScrollOption.java
  23. +10
    -0
      ios/DatePicker.h
  24. +17
    -40
      ios/DatePicker.m
  25. +25
    -0
      ios/RNDatePicker.h
  26. +190
    -0
      ios/RNDatePicker.mm
  27. +0
    -276
      ios/RNDatePicker.xcodeproj/project.pbxproj
  28. +0
    -7
      ios/RNDatePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  29. +0
    -15
      ios/RNDatePicker/DatePicker.h
  30. +0
    -0
      ios/RNDatePickerManager.h
  31. +19
    -7
      ios/RNDatePickerManager.mm
  32. +9
    -1
      package.json
  33. +8
    -1
      react-native-date-picker.podspec
  34. +45
    -17
      src/DatePickerAndroid.js
  35. +9
    -19
      src/DatePickerIOS.js
  36. +12
    -0
      src/fabric/NativeRNDatePicker.ts
  37. +45
    -0
      src/fabric/RNDatePickerNativeComponent.ts
  38. +7
    -5
      src/index.js
  39. +2661
    -4823
      yarn.lock

.github/workflows/maestro.yml → .github/workflows/e2e-tests.yml View File


+ 0
- 124
.github/workflows/test-rn069.yml View File

@ -1,124 +0,0 @@
name: 'Test'
on:
workflow_dispatch:
jobs:
javascript_unit_tests:
name: Unit tests - javascript
runs-on: macos-latest
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 14
cache: 'yarn'
- name: Install npm dependencies
run: |
yarn install --frozen-lockfile
- name: Run unit tests
run: |
yarn test
java_unit_tests:
name: Unit tests - java
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '11'
- name: Install npm dependencies
working-directory: ./examples/Rn069
run: |
yarn install --frozen-lockfile
- name: Run unit tests
working-directory: ./examples/Rn069/android
run: ./gradlew testDebugUnitTest
tests_end_to_end:
name: End to end tests
runs-on: macos-latest
defaults:
run:
working-directory: ./examples/Rn069
steps:
- name: checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14.18.1
cache: 'yarn'
- name: Install npm dependencies (example project)
run: yarn install --frozen-lockfile
- name: Install npm dependencies (root)
run: yarn install --frozen-lockfile
working-directory: ./
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '11'
- name: brew install --cask android-commandlinetools
run: brew install --cask android-commandlinetools
- name: Install emulator
run: |
(while sleep 3; do echo "y"; done) | ~/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager --licenses
echo "✨ Licenses accepted"
~/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager --install emulator
echo "✨ Installed emulator"
~/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager --install "system-images;android-29;default;x86_64"
echo "✨ Installed image"
~/Library/Android/sdk/cmdline-tools/latest/bin/avdmanager --verbose create avd --force --name Pixel_4_API_29 --abi x86_64 --device "pixel" --package "system-images;android-29;default;x86_64"
echo "✨ Created AVD"
- name: launch.sh
uses: nick-fields/retry@v2
with:
timeout_minutes: 3
max_attempts: 3
command: bash ./examples/Rn069/scripts/launch.sh
- name: launch2.sh
run: bash scripts/launch2.sh
- name: butler.sh
run: bash scripts/butler.sh
- name: Build
run: |
yarn build:android:ci
- name: Test
run: |
yarn test:android:ci
- name: Upload artifacts
uses: actions/upload-artifact@v2
if: failure()
with:
name: Failing tests
path: ./examples/Rn069/artifacts
- name: Kill emulator
if: always()
run: |
bash scripts/kill.sh

.github/workflows/android-detox.yml → .github/workflows/unit-tests.yml View File

@ -36,17 +36,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
java-version: 1.8
java-version: 11
distribution: 'temurin'
- name: Install npm dependencies
working-directory: ./examples/detox
working-directory: ./examples/Rn072
run: |
yarn install --frozen-lockfile
- name: Run unit tests
working-directory: ./examples/detox/android
working-directory: ./examples/Rn072/android
run: ./gradlew testDebugUnitTest

+ 6
- 2
README.md View File

@ -126,7 +126,7 @@ export default () => {
| Prop | Description | Screenshots iOS | Screenshot Android |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `date` | The currently selected date. |
| `onDateChange` | Date change handler ( Inline only ) |
| `onDateChange` | Date change handler ( Inline only ) |
| `fadeToColor` | Android picker is fading towards this background color. {color, 'none'} |
| `maximumDate` | Maximum selectable date. <br/> Example: `new Date("2021-12-31")` |
| `minimumDate` | Minimum selectable date. <br/> Example: `new Date("2021-01-01")` |
@ -145,7 +145,7 @@ export default () => {
| `title` | Modal only: Title text. Can be set to null to remove text. |
| `confirmText` | Modal only: Confirm button text. |
| `cancelText` | Modal only: Cancel button text. |
| `theme` | Modal only: The theme of the modal. `"light"`, `"dark"`, `"auto"`. Defaults to `"auto"`. |
| `theme` | Modal only: The theme of the modal. `"light"`, `"dark"`, `"auto"`. Defaults to `"auto"`. |
## Additional android styling
@ -183,6 +183,10 @@ To change the font size on Android `nativeAndroid` variant. Open `styles.xml` an
</style>
```
## React Native's new architecture
This package supports React Native's new architecture (Fabric + Turbo Modules) from React Native 0.71 and forward. Support was introduced in version `4.3.0` of `react-native-date-picker`.
## Linking
This package supports automatic linking. Usually, the only thing you need to do is to install the package, the cocoapods dependencies (as described above). Then rebuild the project by running `react-native run-ios`, `react-native run-android` or start the build from within Xcode/Android Studio. If you're running a React Native version below 0.60 or your setup is having issues with automatic linking, you can run `npx react-native link react-native-date-picker` and rebuild. In some occations you'll have to manually link the package. Instructions in <a href="https://github.com/henninghall/react-native-date-picker/issues/40">this issue</a>.

+ 45
- 1
android/build.gradle View File

@ -1,5 +1,13 @@
apply plugin: 'com.android.library'
def isNewArchitectureEnabled() {
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}
if (isNewArchitectureEnabled()) {
apply plugin: 'com.facebook.react'
}
def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
@ -11,8 +19,18 @@ android {
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', 18)
targetSdkVersion safeExtGet('targetSdkVersion', 25)
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
}
sourceSets {
main {
if (isNewArchitectureEnabled()) {
java.srcDirs += ['src/newarch']
} else {
java.srcDirs += ['src/oldarch']
}
}
}
buildTypes {
release {
@ -22,6 +40,24 @@ android {
}
}
if (isNewArchitectureEnabled()) {
buildscript {
repositories {
mavenCentral()
google()
}
dependencies {
classpath("com.android.tools.build:gradle:7.1.1")
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("de.undercouch:gradle-download-task:5.0.1")
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.facebook.react:react-native:+'
@ -29,3 +65,11 @@ dependencies {
implementation 'org.apache.commons:commons-lang3:3.8'
implementation group: 'net.time4j', name: 'time4j-android', version: '4.8-2021a'
}
if (isNewArchitectureEnabled()) {
react {
jsRootDir = file("../src/")
libraryName = "RNDatePicker"
codegenJavaPackageName = "com.henninghall.date_picker"
}
}

+ 0
- 0
android/gradlew View File


+ 0
- 111
android/src/main/java/com/henninghall/date_picker/DatePickerManager.java View File

@ -1,111 +0,0 @@
package com.henninghall.date_picker;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
import com.henninghall.date_picker.props.DividerHeightProp;
import com.henninghall.date_picker.props.Is24hourSourceProp;
import com.henninghall.date_picker.props.VariantProp;
import com.henninghall.date_picker.props.DateProp;
import com.henninghall.date_picker.props.FadeToColorProp;
import com.henninghall.date_picker.props.LocaleProp;
import com.henninghall.date_picker.props.MaximumDateProp;
import com.henninghall.date_picker.props.MinimumDateProp;
import com.henninghall.date_picker.props.MinuteIntervalProp;
import com.henninghall.date_picker.props.ModeProp;
import com.henninghall.date_picker.props.TextColorProp;
import com.henninghall.date_picker.props.TimezoneOffsetInMinutesProp;
import java.lang.reflect.Method;
import java.util.Map;
public class DatePickerManager extends SimpleViewManager<PickerView> {
private static final String REACT_CLASS = "DatePickerManager";
private static final int SCROLL = 1;
@Override
public String getName() {
return REACT_CLASS;
}
@Override
public PickerView createViewInstance(ThemedReactContext context) {
return new PickerView(new LinearLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT
));
}
@ReactPropGroup(names = { DateProp.name, ModeProp.name, LocaleProp.name, MaximumDateProp.name,
MinimumDateProp.name, FadeToColorProp.name, TextColorProp.name, TimezoneOffsetInMinutesProp.name, MinuteIntervalProp.name,
VariantProp.name, DividerHeightProp.name, Is24hourSourceProp.name
})
public void setProps(PickerView view, int index, Dynamic value) {
updateProp("setProps", view, index, value);
}
@ReactPropGroup(names = {"height"}, customType = "Style")
public void setStyle(PickerView view, int index, Dynamic value) {
updateProp("setStyle", view, index, value);
}
@Override
public Map<String, Integer> getCommandsMap() {
return MapBuilder.of("scroll", SCROLL);
}
@Override
protected void onAfterUpdateTransaction(PickerView pickerView) {
super.onAfterUpdateTransaction(pickerView);
try{
pickerView.update();
}
catch (Exception e){
e.printStackTrace();
}
}
public void receiveCommand(final PickerView view, int command, final ReadableArray args) {
if (command == SCROLL) {
int wheelIndex = args.getInt(0);
int scrollTimes = args.getInt(1);
view.scroll(wheelIndex, scrollTimes);
}
}
public Map getExportedCustomBubblingEventTypeConstants() {
return MapBuilder.builder()
.put("dateChange", MapBuilder.of("phasedRegistrationNames",
MapBuilder.of("bubbled", "onChange")
)
).build();
}
private void updateProp(String methodName, PickerView view, int index, Dynamic value){
String[] propNames = getMethodAnnotation(methodName).names();
String propName = propNames[index];
view.updateProp(propName, value);
}
private ReactPropGroup getMethodAnnotation(String methodName) {
Method[] methods = this.getClass().getMethods();
Method method = null;
for (Method m : methods) {
if (m.getName().equals(methodName))
method = m;
}
return method.getAnnotation(ReactPropGroup.class);
}
}

+ 82
- 0
android/src/main/java/com/henninghall/date_picker/DatePickerManagerImpl.java View File

@ -0,0 +1,82 @@
package com.henninghall.date_picker;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
import java.lang.reflect.Method;
import java.util.Map;
public class DatePickerManagerImpl {
static final String NAME = "RNDatePicker";
static final int SCROLL = 1;
public String getName() {
return NAME;
}
public static PickerView createViewInstance(ThemedReactContext context) {
return new PickerView(new LinearLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT
));
}
public static void setProps(PickerView view, int index, Dynamic value, Class<? extends com.henninghall.date_picker.DatePickerManager> aClass) {
updateProp("setProps", view, index, value, aClass);
}
public static void setStyle(PickerView view, int index, Dynamic value, Class<? extends com.henninghall.date_picker.DatePickerManager> aClass) {
updateProp("setStyle", view, index, value, aClass);
}
public static Map<String, Integer> getCommandsMap() {
return MapBuilder.of("scroll", SCROLL);
}
protected static void onAfterUpdateTransaction(PickerView pickerView) {
try{
pickerView.update();
}
catch (Exception e){
e.printStackTrace();
}
}
public static void receiveCommand(final PickerView view, int commandId, final ReadableArray args) {
if (commandId == SCROLL) {
int wheelIndex = args.getInt(0);
int scrollTimes = args.getInt(1);
view.scroll(wheelIndex, scrollTimes);
}
}
public static void updateProp(String methodName, PickerView view, int index, Dynamic value, Class<? extends com.henninghall.date_picker.DatePickerManager> aClass){
String[] propNames = getMethodAnnotation(methodName, aClass).names();
String propName = propNames[index];
view.updateProp(propName, value);
}
private static ReactPropGroup getMethodAnnotation(String methodName, Class<? extends com.henninghall.date_picker.DatePickerManager> aClass) {
Method[] methods = aClass.getMethods();
Method method = null;
for (Method m : methods) {
if (m.getName().equals(methodName))
method = m;
}
return method.getAnnotation(ReactPropGroup.class);
}
}

+ 127
- 0
android/src/main/java/com/henninghall/date_picker/DatePickerModuleImpl.java View File

@ -0,0 +1,127 @@
package com.henninghall.date_picker;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import net.time4j.android.ApplicationStarter;
public class DatePickerModuleImpl {
public static final String NAME = "RNDatePicker";
private AlertDialog dialog;
DatePickerModuleImpl(Context context) {
ApplicationStarter.initialize(context, false); // false = no need to prefetch on time data background tread
}
public void openPicker(ReadableMap props){
PickerView picker = createPicker(props);
Callback onConfirm = new Callback() {
@Override
public void invoke(Object... objects) {
Emitter.onConfirm(picker.getDate(), picker.getPickerId());
}
};
Callback onCancel = new Callback() {
@Override
public void invoke(Object... objects) {
Emitter.onCancel(picker.getPickerId());
}
};
dialog = createDialog(props, picker, onConfirm, onCancel);
dialog.show();
}
public void closePicker(){
dialog.dismiss();
}
private AlertDialog createDialog(
ReadableMap props, final PickerView picker, final Callback onConfirm, final Callback onCancel) {
String title = props.getString("title");
String confirmText = props.getString("confirmText");
final String cancelText = props.getString("cancelText");
final View pickerWithMargin = withTopMargin(picker);
return new AlertDialog.Builder(DatePickerPackage.context.getCurrentActivity(), getTheme(props))
.setTitle(title)
.setCancelable(true)
.setView(pickerWithMargin)
.setPositiveButton(confirmText, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
onConfirm.invoke(picker.getDate());
dialog.dismiss();
}
})
.setNegativeButton(cancelText, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
onCancel.invoke();
dialog.dismiss();
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
onCancel.invoke();
}
})
.create();
}
private int getTheme(ReadableMap props) {
int defaultTheme = 0;
String theme = props.getString("theme");
if(theme == null) return defaultTheme;
switch (theme){
case "light": return AlertDialog.THEME_DEVICE_DEFAULT_LIGHT;
case "dark": return AlertDialog.THEME_DEVICE_DEFAULT_DARK;
default: return defaultTheme;
}
}
private PickerView createPicker(ReadableMap props){
int height = 180;
LinearLayout.LayoutParams rootLayoutParams = new LinearLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
Utils.toDp(height));
PickerView picker = new PickerView(rootLayoutParams);
ReadableMapKeySetIterator iterator = props.keySetIterator();
while(iterator.hasNextKey()){
String key = iterator.nextKey();
Dynamic value = props.getDynamic(key);
if(!key.equals("style")){
try{
picker.updateProp(key, value);
} catch (Exception e){
// ignore invalid prop
}
}
}
picker.update();
return picker;
}
private View withTopMargin(PickerView view) {
LinearLayout linearLayout = new LinearLayout(DatePickerPackage.context);
linearLayout.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
));
linearLayout.addView(view);
linearLayout.setPadding(0, Utils.toDp(20),0,0);
return linearLayout;
}
}

+ 47
- 8
android/src/main/java/com/henninghall/date_picker/DatePickerPackage.java View File

@ -1,24 +1,55 @@
package com.henninghall.date_picker;
import androidx.annotation.Nullable;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.TurboReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.henninghall.date_picker.DatePickerModule;
import com.henninghall.date_picker.DatePickerManager;
public class DatePickerPackage implements ReactPackage {
public class DatePickerPackage extends TurboReactPackage implements ReactPackage {
public static ReactApplicationContext context;
@Nullable
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
context = reactContext;
return Arrays.<NativeModule>asList(
new DatePickerModule(reactContext)
);
public NativeModule getModule(String name, ReactApplicationContext reactContext) {
if (name.equals(DatePickerModuleImpl.NAME)) {
context = reactContext;
return new DatePickerModule(reactContext);
} else {
return null;
}
}
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
return () -> {
boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
final Map<String, ReactModuleInfo> moduleInfos = new HashMap<>();
moduleInfos.put(
DatePickerModuleImpl.NAME,
new ReactModuleInfo(
DatePickerModuleImpl.NAME,
DatePickerModuleImpl.NAME,
false, // canOverrideExistingModule
false, // needsEagerInit
true, // hasConstants
false, // isCxxModule
isTurboModule
));
return moduleInfos;
};
}
@Override
@ -29,4 +60,12 @@ public class DatePickerPackage implements ReactPackage {
);
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new DatePickerModule(reactContext));
return modules;
}
}

+ 22
- 2
android/src/main/java/com/henninghall/date_picker/Emitter.java View File

@ -19,12 +19,32 @@ public class Emitter {
return DatePickerPackage.context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
}
public static void onDateChange(Calendar date, String displayValueString, View view) {
public static void onDateChange(Calendar date, String displayValueString, String id, View view) {
WritableMap event = Arguments.createMap();
String dateString = Utils.dateToIso(date);
event.putString("date", dateString);
event.putString("dateString", displayValueString);
eventEmitter().receiveEvent(view.getId(), "dateChange", event);
event.putString("id", id);
if(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED){
deviceEventEmitter().emit("dateChange", event);
}
else {
eventEmitter().receiveEvent(view.getId(),"dateChange",event);
}
}
public static void onConfirm(String date, String id) {
WritableMap event = Arguments.createMap();
event.putString("date", date);
event.putString("id", id);
deviceEventEmitter().emit("onConfirm", event);
}
public static void onCancel(String id) {
WritableMap event = Arguments.createMap();
event.putString("id", id);
deviceEventEmitter().emit("onCancel", event);
}
}

+ 4
- 0
android/src/main/java/com/henninghall/date_picker/PickerView.java View File

@ -111,6 +111,10 @@ public class PickerView extends RelativeLayout {
return state.derived.getLastDate();
}
public String getPickerId() {
return state.getId();
}
private final Runnable measureAndLayout = new Runnable() {
@Override
public void run() {

+ 9
- 2
android/src/main/java/com/henninghall/date_picker/State.java View File

@ -5,6 +5,7 @@ import com.henninghall.date_picker.models.Is24HourSource;
import com.henninghall.date_picker.models.Mode;
import com.henninghall.date_picker.models.Variant;
import com.henninghall.date_picker.props.DividerHeightProp;
import com.henninghall.date_picker.props.IdProp;
import com.henninghall.date_picker.props.Is24hourSourceProp;
import com.henninghall.date_picker.props.VariantProp;
import com.henninghall.date_picker.props.DateProp;
@ -41,6 +42,7 @@ public class State {
private final VariantProp variantProp = new VariantProp();
private final DividerHeightProp dividerHeightProp = new DividerHeightProp();
private final Is24hourSourceProp is24hourSourceProp = new Is24hourSourceProp();
private final IdProp idProp = new IdProp();
private final HashMap props = new HashMap<String, Prop>() {{
put(DateProp.name, dateProp);
@ -56,6 +58,7 @@ public class State {
put(VariantProp.name, variantProp);
put(DividerHeightProp.name, dividerHeightProp);
put(Is24hourSourceProp.name, is24hourSourceProp);
put(IdProp.name, idProp);
}};
public DerivedData derived;
@ -100,8 +103,9 @@ public class State {
}
public TimeZone getTimeZone() {
Integer offset = timezoneOffsetInMinutesProp.getValue();
if(offset == null) return TimeZone.getDefault();
String offsetString = timezoneOffsetInMinutesProp.getValue();
if(offsetString == null || offsetString.equals("")) return TimeZone.getDefault();
int offset = Integer.parseInt(offsetString);
int totalOffsetMinutes = Math.abs(offset);
char offsetDirection = offset < 0 ? '-' : '+';
int offsetHours = (int) Math.floor(totalOffsetMinutes / 60f);
@ -147,6 +151,9 @@ public class State {
public int getDividerHeight() {
return dividerHeightProp.getValue();
}
public String getId() {
return idProp.getValue();
}
public Is24HourSource getIs24HourSource() {
return is24hourSourceProp.getValue();

+ 12
- 0
android/src/main/java/com/henninghall/date_picker/props/IdProp.java View File

@ -0,0 +1,12 @@
package com.henninghall.date_picker.props;
import com.facebook.react.bridge.Dynamic;
public class IdProp extends Prop<String> {
public static final String name = "id";
@Override
public String toValue(Dynamic value){
return value.asString();
}
}

+ 3
- 3
android/src/main/java/com/henninghall/date_picker/props/TimezoneOffsetInMinutesProp.java View File

@ -2,12 +2,12 @@ package com.henninghall.date_picker.props;
import com.facebook.react.bridge.Dynamic;
public class TimezoneOffsetInMinutesProp extends Prop<Integer> {
public class TimezoneOffsetInMinutesProp extends Prop<String> {
public static final String name = "timezoneOffsetInMinutes";
@Override
Integer toValue(Dynamic value) {
String toValue(Dynamic value) {
if(value.isNull()) return null;
return value.asInt();
return value.asString();
}
}

+ 1
- 1
android/src/main/java/com/henninghall/date_picker/ui/WheelChangeListenerImpl.java View File

@ -62,7 +62,7 @@ public class WheelChangeListenerImpl implements WheelChangeListener {
String displayData = uiManager.getDisplayValueString();
uiManager.updateLastSelectedDate(selectedDate);
Emitter.onDateChange(selectedDate, displayData, rootView);
Emitter.onDateChange(selectedDate, displayData, state.getId(), rootView);
}
// Example: Jan 1 returns true, April 31 returns false.

+ 65
- 0
android/src/newarch/java/com/henninghall/date_picker/DatePickerManager.java View File

@ -0,0 +1,65 @@
package com.henninghall.date_picker;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
import com.henninghall.date_picker.props.DateProp;
import com.henninghall.date_picker.props.DividerHeightProp;
import com.henninghall.date_picker.props.FadeToColorProp;
import com.henninghall.date_picker.props.IdProp;
import com.henninghall.date_picker.props.Is24hourSourceProp;
import com.henninghall.date_picker.props.LocaleProp;
import com.henninghall.date_picker.props.MaximumDateProp;
import com.henninghall.date_picker.props.MinimumDateProp;
import com.henninghall.date_picker.props.MinuteIntervalProp;
import com.henninghall.date_picker.props.ModeProp;
import com.henninghall.date_picker.props.TextColorProp;
import com.henninghall.date_picker.props.TimezoneOffsetInMinutesProp;
import com.henninghall.date_picker.props.VariantProp;
import java.util.Map;
public class DatePickerManager extends SimpleViewManager<PickerView> {
@Override
public String getName() {
return DatePickerManagerImpl.NAME;
}
@Override
public PickerView createViewInstance(ThemedReactContext context) {
return DatePickerManagerImpl.createViewInstance(context);
}
@ReactPropGroup(names = { DateProp.name, ModeProp.name, LocaleProp.name, MaximumDateProp.name,
MinimumDateProp.name, FadeToColorProp.name, TextColorProp.name, TimezoneOffsetInMinutesProp.name, MinuteIntervalProp.name,
VariantProp.name, DividerHeightProp.name, Is24hourSourceProp.name, IdProp.name
})
public void setProps(PickerView view, int index, Dynamic value) {
DatePickerManagerImpl.updateProp("setProps", view, index, value, getClass());
}
@ReactPropGroup(names = {"height"}, customType = "Style")
public void setStyle(PickerView view, int index, Dynamic value) {
DatePickerManagerImpl.updateProp("setStyle", view, index, value, getClass());
}
@Override
public Map<String, Integer> getCommandsMap() {
return DatePickerManagerImpl.getCommandsMap();
}
@Override
protected void onAfterUpdateTransaction(PickerView pickerView) {
super.onAfterUpdateTransaction(pickerView);
DatePickerManagerImpl.onAfterUpdateTransaction(pickerView);
}
}

android/src/main/java/com/henninghall/date_picker/DatePickerModule.java → android/src/newarch/java/com/henninghall/date_picker/DatePickerModule.java View File

@ -3,12 +3,16 @@ package com.henninghall.date_picker;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.telecom.Call;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
@ -17,8 +21,9 @@ import com.facebook.react.bridge.ReadableMapKeySetIterator;
import net.time4j.android.ApplicationStarter;
public class DatePickerModule extends ReactContextBaseJavaModule {
public class DatePickerModule extends NativeRNDatePickerSpec {
public static final String NAME = "RNDatePicker";
private AlertDialog dialog;
DatePickerModule(ReactApplicationContext context) {
@ -26,19 +31,33 @@ public class DatePickerModule extends ReactContextBaseJavaModule {
ApplicationStarter.initialize(context, false); // false = no need to prefetch on time data background tread
}
@ReactMethod
@Override
public void addListener(String eventName) {
// Keep: Required for RN built in Event Emitter Calls.
}
@ReactMethod
public void removeListeners(Integer count) {
@Override
public void removeListeners() {
// Keep: Required for RN built in Event Emitter Calls.
}
@ReactMethod
public void openPicker(ReadableMap props, Callback onConfirm, Callback onCancel){
@Override
public void openPicker(ReadableMap props){
PickerView picker = createPicker(props);
Callback onConfirm = new Callback() {
@Override
public void invoke(Object... objects) {
Emitter.onConfirm(picker.getDate(), picker.getPickerId());
}
};
Callback onCancel = new Callback() {
@Override
public void invoke(Object... objects) {
Emitter.onCancel(picker.getPickerId());
}
};
dialog = createDialog(props, picker, onConfirm, onCancel);
dialog.show();
}
@ -125,7 +144,8 @@ public class DatePickerModule extends ReactContextBaseJavaModule {
}
@Override
@NonNull
public String getName() {
return "RNDatePicker";
return NAME;
}
}

+ 86
- 0
android/src/oldarch/java/com/henninghall/date_picker/DatePickerManager.java View File

@ -0,0 +1,86 @@
package com.henninghall.date_picker;
import static com.henninghall.date_picker.DatePickerManagerImpl.SCROLL;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
import com.henninghall.date_picker.DatePickerModule;
import com.henninghall.date_picker.props.DividerHeightProp;
import com.henninghall.date_picker.props.Is24hourSourceProp;
import com.henninghall.date_picker.props.VariantProp;
import com.henninghall.date_picker.props.DateProp;
import com.henninghall.date_picker.props.FadeToColorProp;
import com.henninghall.date_picker.props.LocaleProp;
import com.henninghall.date_picker.props.MaximumDateProp;
import com.henninghall.date_picker.props.MinimumDateProp;
import com.henninghall.date_picker.props.MinuteIntervalProp;
import com.henninghall.date_picker.props.ModeProp;
import com.henninghall.date_picker.props.TextColorProp;
import com.henninghall.date_picker.props.TimezoneOffsetInMinutesProp;
import java.util.Map;
public class DatePickerManager extends SimpleViewManager<PickerView> {
@Override
public String getName() {
return DatePickerManagerImpl.NAME;
}
@Override
public PickerView createViewInstance(ThemedReactContext context) {
return DatePickerManagerImpl.createViewInstance(context);
}
@ReactPropGroup(names = { DateProp.name, ModeProp.name, LocaleProp.name, MaximumDateProp.name,
MinimumDateProp.name, FadeToColorProp.name, TextColorProp.name, TimezoneOffsetInMinutesProp.name, MinuteIntervalProp.name,
VariantProp.name, DividerHeightProp.name, Is24hourSourceProp.name
})
public void setProps(PickerView view, int index, Dynamic value) {
DatePickerManagerImpl.setProps(view, index, value, getClass());
}
@ReactPropGroup(names = {"height"}, customType = "Style")
public void setStyle(PickerView view, int index, Dynamic value) {
DatePickerManagerImpl.setStyle(view, index, value, getClass());
}
@Override
public Map<String, Integer> getCommandsMap() {
return DatePickerManagerImpl.getCommandsMap();
}
@Override
protected void onAfterUpdateTransaction(PickerView pickerView) {
super.onAfterUpdateTransaction(pickerView);
DatePickerManagerImpl.onAfterUpdateTransaction(pickerView);
}
public void receiveCommand(final PickerView view, int command, final ReadableArray args) {
DatePickerManagerImpl.receiveCommand(view, command, args);
}
@Override
public Map getExportedCustomBubblingEventTypeConstants() {
return MapBuilder.builder()
.put("dateChange", MapBuilder.of("phasedRegistrationNames",
MapBuilder.of("bubbled", "onChange")
)
).build();
}
}

+ 37
- 0
android/src/oldarch/java/com/henninghall/date_picker/DatePickerModule.java View File

@ -0,0 +1,37 @@
package com.henninghall.date_picker;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
public class DatePickerModule extends ReactContextBaseJavaModule {
private final DatePickerModuleImpl module;
DatePickerModule(ReactApplicationContext context) {
super(context);
module = new DatePickerModuleImpl(context);
}
@ReactMethod
public void addListener(String eventName) {
// Keep: Required for RN built in Event Emitter Calls.
}
@ReactMethod
public void removeListeners(Integer count) {
// Keep: Required for RN built in Event Emitter Calls.
}
@ReactMethod
public void openPicker(ReadableMap props){
module.openPicker(props);
}
@Override
public String getName() {
return DatePickerModuleImpl.NAME;
}
}

+ 3
- 0
examples/Rn072/android/app/build.gradle View File

@ -118,6 +118,9 @@ dependencies {
} else {
implementation jscFlavor
}
androidTestImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.12'
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)

+ 60
- 0
examples/Rn072/android/app/src/test/java/ShortestScrollOption.java View File

@ -0,0 +1,60 @@
import com.henninghall.date_picker.Utils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class ShortestScrollOption {
@Test
public void decreaseOne() {
assertEquals( -1, Utils.getShortestScrollOption(1, 0, 10, false));
}
@Test
public void increaseOne() {
assertEquals(1, Utils.getShortestScrollOption(0, 1, 10, false));
}
@Test
public void increaseFive() {
assertEquals( 5, Utils.getShortestScrollOption(0, 5, 10, false));
}
@Test
public void noChange() {
assertEquals( 0, Utils.getShortestScrollOption(0, 0, 10, false));
}
@Test
public void noWrapping() {
assertEquals( 10, Utils.getShortestScrollOption(0, 10, 10, false));
}
@Test
public void wrapping() {
assertEquals( -1, Utils.getShortestScrollOption(0, 10, 10, true));
}
@Test
public void findingClosestByIncreaseNoWrap() {
assertEquals( 4, Utils.getShortestScrollOption(0, 4, 9, true));
}
@Test
public void findingClosestByIncreaseWrap() {
assertEquals( 4, Utils.getShortestScrollOption(6, 0, 9, true));
}
@Test
public void findingClosestByDecreaseNoWrap() {
assertEquals( -4, Utils.getShortestScrollOption(5, 1, 9, true));
}
@Test
public void findingClosestByDecreaseWrap() {
assertEquals( -4, Utils.getShortestScrollOption(0, 6, 9, true));
}
}

+ 10
- 0
ios/DatePicker.h View File

@ -0,0 +1,10 @@
#import <UIKit/UIKit.h>
@interface DatePicker : UIDatePicker
- (void)setup;
- (void)setTextColorProp:(NSString *)hexColor;
- (void)setTimeZoneOffsetInMinutes:(NSString *)offset;
@end

ios/RNDatePicker/DatePicker.m → ios/DatePicker.m View File

@ -9,6 +9,7 @@
#import "RCTUtils.h"
#import "UIView+React.h"
#import "RCTConvert.h"
@interface DatePicker ()
@ -19,6 +20,15 @@
@implementation DatePicker
-(void)setup {
if(@available(iOS 13, *)) {
self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
}
if(@available(iOS 14, *)) {
self.preferredDatePickerStyle = UIDatePickerStyleWheels;
}
self.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
}
#define UIColorFromRGB(rgbHex) [UIColor colorWithRed:((float)((rgbHex & 0xFF0000) >> 16))/255.0 green:((float)((rgbHex & 0xFF00) >> 8))/255.0 blue:((float)(rgbHex & 0xFF))/255.0 alpha:1.0]
@ -46,25 +56,6 @@
return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
}
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
[self addTarget:self action:@selector(didChange)
forControlEvents:UIControlEventValueChanged];
if(@available(iOS 13, *)) {
self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
}
if(@available(iOS 14, *)) {
self.preferredDatePickerStyle = UIDatePickerStyleWheels;
}
_reactMinuteInterval = 1;
// only allow gregorian calendar
self.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
}
return self;
}
- (void)setColor:(NSString *)hexColor {
// Hex to int color
unsigned intColor = 0;
@ -86,7 +77,6 @@
- (void)setTextColorProp:(NSString *)hexColor
{
if(@available(iOS 13, *)) {
// black text -> set light mode
@ -111,29 +101,16 @@
}
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)didChange
- (void)setTimeZoneOffsetInMinutes:(NSString *)timeZoneOffsetInMinutes
{
if (_onChange) {
_onChange(@{ @"timestamp": @(self.date.timeIntervalSince1970 * 1000.0) });
if([timeZoneOffsetInMinutes length] == 0){
[self setTimeZone: nil];
}
else {
NSNumber *timezoneMinutesInt = [NSNumber numberWithInt:[timeZoneOffsetInMinutes intValue]];
[self setTimeZone:[RCTConvert NSTimeZone: timezoneMinutesInt]];
}
}
- (void)setDatePickerMode:(UIDatePickerMode)datePickerMode
{
[super setDatePickerMode:datePickerMode];
// We need to set minuteInterval after setting datePickerMode, otherwise minuteInterval is invalid in time mode.
self.minuteInterval = _reactMinuteInterval;
}
- (void)setMinuteInterval:(NSInteger)minuteInterval
{
[super setMinuteInterval:minuteInterval];
_reactMinuteInterval = minuteInterval;
}
@end

+ 25
- 0
ios/RNDatePicker.h View File

@ -0,0 +1,25 @@
#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTViewComponentView.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface RNDatePicker : RCTViewComponentView
@end
NS_ASSUME_NONNULL_END
#else
#import "DatePicker.h"
#import <UIKit/UIKit.h>
@interface RNDatePicker : DatePicker
@end
#endif

+ 190
- 0
ios/RNDatePicker.mm View File

@ -0,0 +1,190 @@
#import "RNDatePicker.h"
#ifdef RCT_NEW_ARCH_ENABLED
#import "RCTConvert.h"
#import <React/RCTConversions.h>
#import <react/renderer/components/RNDatePickerSpecs/ComponentDescriptors.h>
#import <react/renderer/components/RNDatePickerSpecs/EventEmitters.h>
#import <react/renderer/components/RNDatePickerSpecs/Props.h>
#import <react/renderer/components/RNDatePickerSpecs/RCTComponentViewHelpers.h>
#import "RCTFabricComponentsPlugins.h"
using namespace facebook::react;
#else
#import "RCTUtils.h"
#import "UIView+React.h"
#import "RCTComponent.h"
#endif
#import "DatePicker.h"
#ifdef RCT_NEW_ARCH_ENABLED
@interface RNDatePicker () <RCTRNDatePickerViewProtocol>
@end
#else
@interface RNDatePicker ()
@property (nonatomic, copy) RCTBubblingEventBlock onChange;
@property (nonatomic, assign) NSInteger reactMinuteInterval;
@end
#endif
@implementation RNDatePicker {
DatePicker *_picker;
UIView *_view;
UILabel *_label;
NSInteger _reactMinuteInterval;
}
#ifdef RCT_NEW_ARCH_ENABLED
+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<RNDatePickerComponentDescriptor>();
}
#endif
NSDate* unixMillisToNSDate (double unixMillis) {
double time = unixMillis/1000.0;
return [NSDate dateWithTimeIntervalSince1970: time];
}
#ifdef RCT_NEW_ARCH_ENABLED
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
static const auto defaultProps = std::make_shared<const RNDatePickerProps>();
_props = defaultProps;
_picker = [[DatePicker alloc] initWithFrame:_view.bounds];
[_picker setup];
[_picker addTarget:self action:@selector(didChange:)
forControlEvents:UIControlEventValueChanged];
_reactMinuteInterval = 1;
self.contentView = _picker;
}
return self;
}
#else
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
[self setup];
[self addTarget:self action:@selector(didChange)
forControlEvents:UIControlEventValueChanged];
_reactMinuteInterval = 1;
}
return self;
}
#endif
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
#ifdef RCT_NEW_ARCH_ENABLED
- (void)setContentView:(UIView *)contentView
{
[super setContentView:_picker];
}
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &oldViewProps = *std::static_pointer_cast<RNDatePickerProps const>(oldProps ? oldProps : _props); //_props equ
const auto &newViewProps = *std::static_pointer_cast<RNDatePickerProps const>(props);
// date
if(oldViewProps.date != newViewProps.date) {
[_picker setDate: unixMillisToNSDate(newViewProps.date)];
}
// locale
if(oldViewProps.locale != newViewProps.locale) {
NSString *convertedLocale = RCTNSStringFromString(newViewProps.locale);
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:convertedLocale];
[_picker setLocale:locale];
}
// maximumDate
if(oldViewProps.maximumDate != newViewProps.maximumDate) {
[_picker setMaximumDate: unixMillisToNSDate(newViewProps.maximumDate)];
}
// minimumDate
if(oldViewProps.minimumDate != newViewProps.minimumDate) {
[_picker setMinimumDate: unixMillisToNSDate(newViewProps.minimumDate)];
}
// setMinuteInterval
if (oldViewProps.minuteInterval != newViewProps.minuteInterval) {
[_picker setMinuteInterval:newViewProps.minuteInterval];
_reactMinuteInterval = newViewProps.minuteInterval;
}
// mode
if (oldViewProps.mode != newViewProps.mode) {
if(newViewProps.mode == RNDatePickerMode::Time) [_picker setDatePickerMode:UIDatePickerModeTime];
if(newViewProps.mode == RNDatePickerMode::Date) [_picker setDatePickerMode:UIDatePickerModeDate];
if(newViewProps.mode == RNDatePickerMode::Datetime) [_picker setDatePickerMode:UIDatePickerModeDateAndTime];
// We need to set minuteInterval after setting datePickerMode, otherwise minuteInterval is invalid in time mode.
_picker.minuteInterval = _reactMinuteInterval;
}
// timeZoneOffsetInMinutes
if (oldViewProps.timeZoneOffsetInMinutes != newViewProps.timeZoneOffsetInMinutes) {
NSString *newString = RCTNSStringFromString(newViewProps.timeZoneOffsetInMinutes);
[_picker setTimeZoneOffsetInMinutes:newString];
}
// text color
if(oldViewProps.textColor != newViewProps.textColor){
NSString *textColor = RCTNSStringFromString(newViewProps.textColor);
[_picker setTextColorProp:textColor];
}
[super updateProps:props oldProps:oldProps];
}
-(void)didChange:(RNDatePicker *)sender
{
std::dynamic_pointer_cast<const RNDatePickerEventEmitter>(_eventEmitter)
->onChange(RNDatePickerEventEmitter::OnChange{ .timestamp = _picker.date.timeIntervalSince1970 * 1000.0f });
}
Class<RCTComponentViewProtocol> RNDatePickerCls(void)
{
return RNDatePicker.class;
}
#else
- (void)didChange
{
if (_onChange) {
_onChange(@{ @"timestamp": @(self.date.timeIntervalSince1970 * 1000.0) });
}
}
- (void)setDatePickerMode:(UIDatePickerMode)datePickerMode
{
[super setDatePickerMode:datePickerMode];
// We need to set minuteInterval after setting datePickerMode, otherwise minuteInterval is invalid in time mode.
self.minuteInterval = _reactMinuteInterval;
}
- (void)setMinuteInterval:(NSInteger)minuteInterval
{
[super setMinuteInterval:minuteInterval];
_reactMinuteInterval = minuteInterval;
}
#endif
@end

+ 0
- 276
ios/RNDatePicker.xcodeproj/project.pbxproj View File

@ -1,276 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
5B5A664D2130B82E00599381 /* RNDatePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B5A664B2130B82D00599381 /* RNDatePickerManager.m */; };
DA5891DC1BA9A9FC002B4DB2 /* DatePicker.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DA5891DB1BA9A9FC002B4DB2 /* DatePicker.h */; };
DA5891DE1BA9A9FC002B4DB2 /* DatePicker.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5891DD1BA9A9FC002B4DB2 /* DatePicker.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
DA5891D61BA9A9FC002B4DB2 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
DA5891DC1BA9A9FC002B4DB2 /* DatePicker.h in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
5B5A664B2130B82D00599381 /* RNDatePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNDatePickerManager.m; sourceTree = "<group>"; };
5B5A664C2130B82E00599381 /* RNDatePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNDatePickerManager.h; sourceTree = "<group>"; };
DA5891D81BA9A9FC002B4DB2 /* libRNDatePicker.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNDatePicker.a; sourceTree = BUILT_PRODUCTS_DIR; };
DA5891DB1BA9A9FC002B4DB2 /* DatePicker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DatePicker.h; sourceTree = "<group>"; };
DA5891DD1BA9A9FC002B4DB2 /* DatePicker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DatePicker.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
DA5891D51BA9A9FC002B4DB2 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
DA5891CF1BA9A9FC002B4DB2 = {
isa = PBXGroup;
children = (
DA5891DA1BA9A9FC002B4DB2 /* RNDatePicker */,
DA5891D91BA9A9FC002B4DB2 /* Products */,
);
sourceTree = "<group>";
};
DA5891D91BA9A9FC002B4DB2 /* Products */ = {
isa = PBXGroup;
children = (
DA5891D81BA9A9FC002B4DB2 /* libRNDatePicker.a */,
);
name = Products;
sourceTree = "<group>";
};
DA5891DA1BA9A9FC002B4DB2 /* RNDatePicker */ = {
isa = PBXGroup;
children = (
5B5A664C2130B82E00599381 /* RNDatePickerManager.h */,
5B5A664B2130B82D00599381 /* RNDatePickerManager.m */,
DA5891DB1BA9A9FC002B4DB2 /* DatePicker.h */,
DA5891DD1BA9A9FC002B4DB2 /* DatePicker.m */,
);
path = RNDatePicker;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
DA5891D71BA9A9FC002B4DB2 /* RNDatePicker */ = {
isa = PBXNativeTarget;
buildConfigurationList = DA5891E11BA9A9FC002B4DB2 /* Build configuration list for PBXNativeTarget "RNDatePicker" */;
buildPhases = (
DA5891D41BA9A9FC002B4DB2 /* Sources */,
DA5891D51BA9A9FC002B4DB2 /* Frameworks */,
DA5891D61BA9A9FC002B4DB2 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = RNDatePicker;
productName = RNDeviceInfo;
productReference = DA5891D81BA9A9FC002B4DB2 /* libRNDatePicker.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
DA5891D01BA9A9FC002B4DB2 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = Learnium;
TargetAttributes = {
DA5891D71BA9A9FC002B4DB2 = {
CreatedOnToolsVersion = 7.0;
};
};
};
buildConfigurationList = DA5891D31BA9A9FC002B4DB2 /* Build configuration list for PBXProject "RNDatePicker" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = DA5891CF1BA9A9FC002B4DB2;
productRefGroup = DA5891D91BA9A9FC002B4DB2 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
DA5891D71BA9A9FC002B4DB2 /* RNDatePicker */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
DA5891D41BA9A9FC002B4DB2 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DA5891DE1BA9A9FC002B4DB2 /* DatePicker.m in Sources */,
5B5A664D2130B82E00599381 /* RNDatePickerManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
DA5891DF1BA9A9FC002B4DB2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
DA5891E01BA9A9FC002B4DB2 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
DA5891E21BA9A9FC002B4DB2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(BUILT_PRODUCTS_DIR)/usr/local/include",
"$(SRCROOT)/../../React/**",
"$(SRCROOT)/node_modules/react-native/React/**",
"$(SRCROOT)/../react-native/React/**",
"$(SRCROOT)/../../../node_modules/react-native/React/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Debug;
};
DA5891E31BA9A9FC002B4DB2 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(BUILT_PRODUCTS_DIR)/usr/local/include",
"$(SRCROOT)/../../React/**",
"$(SRCROOT)/node_modules/react-native/React/**",
"$(SRCROOT)/../react-native/React/**",
"$(SRCROOT)/../../../node_modules/react-native/React/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
DA5891D31BA9A9FC002B4DB2 /* Build configuration list for PBXProject "RNDatePicker" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DA5891DF1BA9A9FC002B4DB2 /* Debug */,
DA5891E01BA9A9FC002B4DB2 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DA5891E11BA9A9FC002B4DB2 /* Build configuration list for PBXNativeTarget "RNDatePicker" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DA5891E21BA9A9FC002B4DB2 /* Debug */,
DA5891E31BA9A9FC002B4DB2 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = DA5891D01BA9A9FC002B4DB2 /* Project object */;
}

+ 0
- 7
ios/RNDatePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:/Users/henninghall/Projects/react-native-date-picker/example/node_modules/react-native-date-picker-x/ios/DatePicker.xcodeproj">
</FileRef>
</Workspace>

+ 0
- 15
ios/RNDatePicker/DatePicker.h View File

@ -1,15 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
@interface DatePicker : UIDatePicker
- (void)setTextColorProp:(NSString *)hexColor;
@end

ios/RNDatePicker/RNDatePickerManager.h → ios/RNDatePickerManager.h View File


ios/RNDatePicker/RNDatePickerManager.m → ios/RNDatePickerManager.mm View File

@ -1,3 +1,9 @@
#import <React/RCTLog.h>
#import <React/RCTUIManager.h>
#import <React/RCTViewManager.h>
#import "RNDatePickerManager.h"
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
@ -5,12 +11,12 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNDatePickerManager.h"
#import <React/RCTLog.h>
#import "RCTConvert.h"
#import "DatePicker.h"
#import "RNDatePicker.h"
@implementation RCTConvert(UIDatePicker)
@ -25,7 +31,7 @@ RCT_ENUM_CONVERTER(UIDatePickerMode, (@{
@implementation RNDatePickerManager
RCT_EXPORT_MODULE()
RCT_EXPORT_MODULE(RNDatePicker)
RCT_EXPORT_METHOD(addListener : (NSString *)eventName) {
// Keep: Required for RN built in Event Emitter Calls.
@ -37,9 +43,10 @@ RCT_EXPORT_METHOD(removeListeners : (NSInteger)count) {
- (UIView *)view
{
return [DatePicker new];
return [RNDatePicker new];
}
RCT_EXPORT_VIEW_PROPERTY(text, NSString)
RCT_EXPORT_VIEW_PROPERTY(date, NSDate)
RCT_EXPORT_VIEW_PROPERTY(locale, NSLocale)
RCT_EXPORT_VIEW_PROPERTY(minimumDate, NSDate)
@ -47,8 +54,11 @@ RCT_EXPORT_VIEW_PROPERTY(maximumDate, NSDate)
RCT_EXPORT_VIEW_PROPERTY(minuteInterval, NSInteger)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
RCT_REMAP_VIEW_PROPERTY(mode, datePickerMode, UIDatePickerMode)
RCT_REMAP_VIEW_PROPERTY(timeZoneOffsetInMinutes, timeZone, NSTimeZone)
RCT_CUSTOM_VIEW_PROPERTY(timeZoneOffsetInMinutes, NSString, DatePicker)
{
[view setTimeZoneOffsetInMinutes:[RCTConvert NSString:json]];
}
RCT_CUSTOM_VIEW_PROPERTY(textColor, NSString, DatePicker)
{
@ -69,6 +79,8 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *) props
NSString * confirmText = [RCTConvert NSString:[props objectForKey:@"confirmText"]];
NSString * cancelText = [RCTConvert NSString:[props objectForKey:@"cancelText"]];
DatePicker* picker = [[DatePicker alloc] init];
[picker setup];
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIView * alertView = alertController.view;
@ -115,9 +127,7 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *) props
[picker setMinuteInterval:minuteInterval];
NSString * timeZoneProp = [props valueForKey:@"timeZoneOffsetInMinutes"];
if(timeZoneProp){
[picker setTimeZone:[RCTConvert NSTimeZone:timeZoneProp]];
}
if(timeZoneProp) [picker setTimeZoneOffsetInMinutes:timeZoneProp];
if(@available(iOS 13, *)) {
NSString * _Nonnull theme = [RCTConvert NSString:[props objectForKey:@"theme"]];
@ -187,3 +197,5 @@ RCT_EXPORT_METHOD(closePicker)
}
@end

+ 9
- 1
package.json View File

@ -1,6 +1,6 @@
{
"name": "react-native-date-picker",
"version": "4.2.14",
"version": "4.3.0-alpha.11",
"description": "A datetime picker for React Native. In-modal or inlined. Supports Android and iOS.",
"source": "src/index",
"main": "src/index.js",
@ -48,5 +48,13 @@
"babel-jest": "^26.6.3",
"jest": "^26.6.3",
"prettier": "^2.2.1"
},
"codegenConfig": {
"name": "RNDatePickerSpecs",
"type": "all",
"jsSrcsDir": "src/fabric",
"android": {
"javaPackageName": "com.henninghall.date_picker"
}
}
}

+ 8
- 1
react-native-date-picker.podspec View File

@ -2,6 +2,9 @@ require 'json'
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
Pod::Spec.new do |s|
s.name = "react-native-date-picker"
s.version = package['version']
@ -13,7 +16,11 @@ Pod::Spec.new do |s|
s.platform = :ios, "8.0"
s.source = { :git => "https://github.com/henninghall/react-native-date-picker.git" }
s.source_files = "ios/RNDatePicker/*.{h,m}"
s.source_files = "ios/**/*.{h,m,mm,swift}"
s.dependency 'React-Core'
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
install_modules_dependencies(s)
end
end

+ 45
- 17
src/DatePickerAndroid.js View File

@ -1,14 +1,16 @@
import React from 'react'
import { NativeModules, requireNativeComponent, Platform } from 'react-native'
import {
NativeEventEmitter,
NativeModules,
Platform,
TurboModuleRegistry,
requireNativeComponent,
} from 'react-native'
import { shouldCloseModal, shouldOpenModal } from './modal'
function addMinutes(date, minutesToAdd) {
return new Date(date.valueOf() + minutesToAdd * 60 * 1000)
}
const NativeDatePicker =
Platform.OS === 'android'
? requireNativeComponent(`DatePickerManager`, DatePickerAndroid, {
? requireNativeComponent('RNDatePicker', DatePickerAndroid, {
nativeOnly: { onChange: true },
})
: null
@ -17,21 +19,21 @@ const height = 180
const timeModeWidth = 240
const defaultWidth = 310
const NativePicker = TurboModuleRegistry
? TurboModuleRegistry.get('RNDatePicker')
: NativeModules.RNDatePicker
class DatePickerAndroid extends React.PureComponent {
render() {
const props = this.getProps()
if (shouldOpenModal(props, this.previousProps)) {
this.isClosing = false
NativeModules.RNDatePicker.openPicker(
props,
this._onConfirm,
this._onCancel
)
NativePicker.openPicker(props)
}
if (shouldCloseModal(props, this.previousProps, this.isClosing)) {
this.closing = true
NativeModules.RNDatePicker.closePicker()
NativePicker.closePicker()
}
this.previousProps = props
@ -41,9 +43,30 @@ class DatePickerAndroid extends React.PureComponent {
return <NativeDatePicker {...props} onChange={this._onChange} />
}
getId = () => {
if (!this.id) {
this.id = Math.random().toString()
}
return this.id
}
componentDidMount = () => {
this.eventEmitter = new NativeEventEmitter(NativePicker)
this.eventEmitter.addListener('dateChange', this._onChange)
this.eventEmitter.addListener('onConfirm', this._onConfirm)
this.eventEmitter.addListener('onCancel', this._onCancel)
}
componentWillUnmount = () => {
this.eventEmitter.removeAllListeners('dateChange')
this.eventEmitter.removeAllListeners('onConfirm')
this.eventEmitter.removeAllListeners('onCancel')
}
getProps = () => ({
...this.props,
date: this._date(),
id: this.getId(),
minimumDate: this._minimumDate(),
maximumDate: this._maximumDate(),
timezoneOffsetInMinutes: this._getTimezoneOffsetInMinutes(),
@ -61,10 +84,13 @@ class DatePickerAndroid extends React.PureComponent {
}
_onChange = (e) => {
const jsDate = this._fromIsoWithTimeZoneOffset(e.nativeEvent.date)
const { date, id, dateString } = e.nativeEvent ?? e
const newArch = id !== null
if (newArch && id !== this.id) return
const jsDate = this._fromIsoWithTimeZoneOffset(date)
this.props.onDateChange && this.props.onDateChange(jsDate)
if (this.props.onDateStringChange) {
this.props.onDateStringChange(e.nativeEvent.dateString)
this.props.onDateStringChange(dateString)
}
}
@ -86,12 +112,14 @@ class DatePickerAndroid extends React.PureComponent {
return date.toISOString()
}
_onConfirm = (isoDate) => {
_onConfirm = ({ date, id }) => {
if (id !== this.id) return
this.isClosing = true
this.props.onConfirm(this._fromIsoWithTimeZoneOffset(isoDate))
this.props.onConfirm(this._fromIsoWithTimeZoneOffset(date))
}
_onCancel = () => {
_onCancel = ({ id }) => {
if (id !== this.id) return
this.isClosing = true
this.props.onCancel()
}

+ 9
- 19
src/DatePickerIOS.js View File

@ -1,24 +1,17 @@
import React from 'react'
import { StyleSheet, requireNativeComponent, NativeModules, Platform } from 'react-native'
import {
NativeModules,
Platform,
StyleSheet,
requireNativeComponent,
} from 'react-native'
import { shouldCloseModal, shouldOpenModal } from './modal'
// TODO: Move to its own file
const RCTDatePickerIOS =
Platform.OS === 'ios' ? requireNativeComponent('RNDatePicker') : null
export default class DatePickerIOS extends React.Component {
_picker = null
componentDidUpdate() {
if (this.props.date) {
const propsTimeStamp = this.props.date.getTime()
if (this._picker) {
this._picker.setNativeProps({
date: propsTimeStamp,
})
}
}
}
_onChange = (event) => {
const nativeTimeStamp = event.nativeEvent.timestamp
this.props.onDateChange &&
@ -52,7 +45,7 @@ export default class DatePickerIOS extends React.Component {
if (shouldOpenModal(props, this.previousProps)) {
this.isClosing = false
NativeModules.RNDatePickerManager.openPicker(
NativeModules.RNDatePicker.openPicker(
props,
this._onConfirm,
this._onCancel
@ -60,7 +53,7 @@ export default class DatePickerIOS extends React.Component {
}
if (shouldCloseModal(props, this.previousProps, this.isClosing)) {
this.isClosing = true
NativeModules.RNDatePickerManager.closePicker()
NativeModules.RNDatePicker.closePicker()
}
this.previousProps = props
@ -70,9 +63,6 @@ export default class DatePickerIOS extends React.Component {
return (
<RCTDatePickerIOS
key={props.textColor} // preventing "Today" string keep old text color when text color changes
ref={(picker) => {
this._picker = picker
}}
onChange={this._onChange}
onStartShouldSetResponder={() => true}
onResponderTerminationRequest={() => false}

+ 12
- 0
src/fabric/NativeRNDatePicker.ts View File

@ -0,0 +1,12 @@
import { TurboModuleRegistry } from 'react-native'
import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport'
import { UnsafeObject } from 'react-native/Libraries/Types/CodegenTypes'
export interface Spec extends TurboModule {
readonly getConstants: () => {}
openPicker(props: UnsafeObject): void
removeListeners(): void
addListener(eventName: string): void
}
export default TurboModuleRegistry.get<Spec>('RNDatePicker')

+ 45
- 0
src/fabric/RNDatePickerNativeComponent.ts View File

@ -0,0 +1,45 @@
import type { HostComponent, ViewProps } from 'react-native'
import {
BubblingEventHandler,
Double,
Int32,
WithDefault,
} from 'react-native/Libraries/Types/CodegenTypes'
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'
type DateEvent = {
timestamp: Double
}
export interface NativeProps extends ViewProps {
locale?: string
date: Double
maximumDate?: Double
minimumDate?: Double
minuteInterval?: Int32
androidVariant?: WithDefault<'iosClone' | 'nativeAndroid', 'nativeAndroid'>
mode?: WithDefault<'date' | 'time' | 'datetime', 'datetime'>
onChange: BubblingEventHandler<DateEvent>
// Type has to be string to allow null/undefined as value.
// For timezoneOffset, undefined and 0 means different things. 0 means GMT and undefined means device timezone.
timeZoneOffsetInMinutes?: string | null
fadeToColor?: string
textColor?: string
dividerHeight?: Int32
is24hourSource?: WithDefault<'locale' | 'device', 'device'>
theme?: WithDefault<'light' | 'dark' | 'auto', 'auto'>
// Modal props
modal?: boolean
open?: boolean
onConfirm?: BubblingEventHandler<DateEvent>
onCancel?: BubblingEventHandler<undefined>
confirmText?: string
cancelText?: string
title?: string
}
export default codegenNativeComponent<NativeProps>(
'RNDatePicker'
) as HostComponent<NativeProps>

+ 7
- 5
src/index.js View File

@ -1,10 +1,9 @@
import React from 'react'
import { Platform, Appearance, Text } from 'react-native'
import { Appearance, Platform, Text } from 'react-native'
import DatePickerAndroid from './DatePickerAndroid'
import propTypes from './propTypes'
import DatePickerIOS from './DatePickerIOS'
import { colorToHex } from './colorToHex'
import { throwIfInvalidProps } from './propChecker'
import DatePickerIOS from './DatePickerIOS'
const DatePicker = Platform.select({
android: DatePickerAndroid,
@ -12,8 +11,6 @@ const DatePicker = Platform.select({
default: () => <Text>DatePicker is not supported on this platform.</Text>,
})
DatePicker.propTypes = propTypes
const DatePickerWrapper = (props) => {
if (__DEV__) throwIfInvalidProps(props)
return (
@ -29,6 +26,11 @@ const DatePickerWrapper = (props) => {
androidVariant={getAndroidVariant(props)}
minuteInterval={props.minuteInterval ? props.minuteInterval : 1}
mode={props.mode ? props.mode : 'datetime'}
timeZoneOffsetInMinutes={
props.timeZoneOffsetInMinutes == null
? ''
: props.timeZoneOffsetInMinutes.toString()
}
/>
)
}

+ 2661
- 4823
yarn.lock
File diff suppressed because it is too large
View File


Loading…
Cancel
Save