diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c880b170..bf8c37c0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -68,3 +68,30 @@ An existing Android emulator is required to match the name defined in `detox.con
yarn detox:android:build:release
yarn detox:android:test:release
```
+
+### Fabric
+
+Fabric is the new React Native rendering system ([read more about it here](https://reactnative.dev/architecture/fabric-renderer)).
+
+#### iOS
+
+```
+yarn start
+cd "example/ios" && RCT_NEW_ARCH_ENABLED=1 npx pod-install && cd -
+yarn start:ios
+```
+
+If you want to go back to the old renderer (Paper),
+remove `ios/build`, run `pod-install` without the `RCT_NEW_ARCH_ENABLED=1` and build again
+
+```
+rm -r "example/ios/build"
+cd "example/ios" && npx pod-install && cd -
+yarn start:ios
+```
+
+
+#### Android
+
+The date time picker does not have a native UI component for Android but a native module.
+([read more about native modules here](https://reactnative.dev/docs/native-modules-intro)).
\ No newline at end of file
diff --git a/RNDateTimePicker.podspec b/RNDateTimePicker.podspec
index ae7cb4db..87fe7173 100644
--- a/RNDateTimePicker.podspec
+++ b/RNDateTimePicker.podspec
@@ -1,5 +1,7 @@
require 'json'
+fabric_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
Pod::Spec.new do |s|
@@ -12,8 +14,28 @@ Pod::Spec.new do |s|
s.homepage = package['homepage']
s.platform = :ios, "11.0"
s.source = { :git => "https://github.com/react-native-community/datetimepicker", :tag => "v#{s.version}" }
- s.source_files = "ios/*.{h,m}"
+ s.source_files = "ios/**/*.{h,m,mm}"
s.requires_arc = true
- s.dependency "React-Core"
+ if fabric_enabled
+ folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
+
+ s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
+ s.pod_target_xcconfig = {
+ 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/boost" "$(PODS_ROOT)/boost-for-react-native" "$(PODS_ROOT)/RCT-Folly"',
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
+ }
+
+ s.dependency "React"
+ s.dependency "React-RCTFabric"
+ s.dependency "React-Codegen"
+ s.dependency "RCT-Folly"
+ s.dependency "RCTRequired"
+ s.dependency "RCTTypeSafety"
+ s.dependency "ReactCommon/turbomodule/core"
+ else
+ s.exclude_files = "ios/fabric"
+
+ s.dependency "React-Core"
+ end
end
diff --git a/example/App.js b/example/App.js
index ed6017da..c5121131 100644
--- a/example/App.js
+++ b/example/App.js
@@ -12,7 +12,7 @@ import {
Switch,
} from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker';
-import SegmentedControl from '@react-native-segmented-control/segmented-control';
+import SegmentedControl from './SegmentedControl';
import {Colors} from 'react-native/Libraries/NewAppScreen';
import React, {useRef, useState} from 'react';
import {Picker} from 'react-native-windows';
diff --git a/example/SegmentedControl.js b/example/SegmentedControl.js
new file mode 100644
index 00000000..9186d263
--- /dev/null
+++ b/example/SegmentedControl.js
@@ -0,0 +1,7 @@
+import {isFabricEnabled} from '../src/utils';
+
+import SegmentedControl from '@react-native-segmented-control/segmented-control';
+import JSSegmentedControl from '@react-native-segmented-control/segmented-control/js/SegmentedControl.js';
+
+// Forcing the JS implementation for Fabric as the native module is not compatible with Fabric yet.
+export default isFabricEnabled ? JSSegmentedControl : SegmentedControl;
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index 856293bb..e6a644b4 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -570,7 +570,7 @@ SPEC CHECKSUMS:
ReactCommon: de55f940495d7bf87b5d7bf55b5b15cdd50d7d7b
ReactTestApp-DevSupport: 8a8cff38c37cd8145a12ac7d7d0503dd08f97d65
ReactTestApp-Resources: ff5f151e465e890010b417ce65ca6c5de6aeccbb
- RNDateTimePicker: 4f1fc917f5af9d9ae4c5fc0c63a4474d61338693
+ RNDateTimePicker: 9d66f002d6095cc89fcb66d0dc54bc6191c9ab0d
RNLocalize: cbcb55d0e19c78086ea4eea20e03fe8000bbbced
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
Yoga: 82c9e8f652789f67d98bed5aef9d6653f71b04a9
diff --git a/example/ios/date-time-picker-example.xcworkspace/contents.xcworkspacedata b/example/ios/date-time-picker-example.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..7b3c0ff9
--- /dev/null
+++ b/example/ios/date-time-picker-example.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/example/ios/date-time-picker-example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/date-time-picker-example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/example/ios/date-time-picker-example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ios/RNDateTimePicker.xcodeproj/project.pbxproj b/ios/RNDateTimePicker.xcodeproj/project.pbxproj
index be6be629..980cdd3d 100644
--- a/ios/RNDateTimePicker.xcodeproj/project.pbxproj
+++ b/ios/RNDateTimePicker.xcodeproj/project.pbxproj
@@ -6,11 +6,6 @@
objectVersion = 46;
objects = {
-/* Begin PBXBuildFile section */
- 1415EF09223110200027D3C6 /* RNDateTimePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1415EF07223110200027D3C6 /* RNDateTimePickerManager.m */; };
- B3E7B58A1CC2AC0600A0062D /* RNDateTimePicker.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNDateTimePicker.m */; };
-/* End PBXBuildFile section */
-
/* Begin PBXCopyFilesBuildPhase section */
58B511D91A9E6C8500147676 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
@@ -25,10 +20,11 @@
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRNDateTimePicker.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNDateTimePicker.a; sourceTree = BUILT_PRODUCTS_DIR; };
- 1415EF07223110200027D3C6 /* RNDateTimePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNDateTimePickerManager.m; sourceTree = ""; };
- 1415EF08223110200027D3C6 /* RNDateTimePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNDateTimePickerManager.h; sourceTree = ""; };
- B3E7B5881CC2AC0600A0062D /* RNDateTimePicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNDateTimePicker.h; sourceTree = ""; };
- B3E7B5891CC2AC0600A0062D /* RNDateTimePicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNDateTimePicker.m; sourceTree = ""; };
+ 7C1CCA3B28D0D66200DABF4D /* fabric */ = {isa = PBXFileReference; lastKnownFileType = folder; path = fabric; sourceTree = ""; };
+ 7C1CCA3D28D227F800DABF4D /* RNDateTimePicker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNDateTimePicker.m; sourceTree = ""; };
+ 7C1CCA3E28D227F800DABF4D /* RNDateTimePicker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNDateTimePicker.h; sourceTree = ""; };
+ 7C1CCA3F28D227F900DABF4D /* RNDateTimePickerManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNDateTimePickerManager.h; sourceTree = ""; };
+ 7C1CCA4028D227F900DABF4D /* RNDateTimePickerManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNDateTimePickerManager.m; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -53,10 +49,11 @@
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
- 1415EF08223110200027D3C6 /* RNDateTimePickerManager.h */,
- 1415EF07223110200027D3C6 /* RNDateTimePickerManager.m */,
- B3E7B5881CC2AC0600A0062D /* RNDateTimePicker.h */,
- B3E7B5891CC2AC0600A0062D /* RNDateTimePicker.m */,
+ 7C1CCA3E28D227F800DABF4D /* RNDateTimePicker.h */,
+ 7C1CCA3D28D227F800DABF4D /* RNDateTimePicker.m */,
+ 7C1CCA3F28D227F900DABF4D /* RNDateTimePickerManager.h */,
+ 7C1CCA4028D227F900DABF4D /* RNDateTimePickerManager.m */,
+ 7C1CCA3B28D0D66200DABF4D /* fabric */,
134814211AA4EA7D00B7C361 /* Products */,
);
sourceTree = "";
@@ -118,8 +115,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 1415EF09223110200027D3C6 /* RNDateTimePickerManager.m in Sources */,
- B3E7B58A1CC2AC0600A0062D /* RNDateTimePicker.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/ios/fabric/RNDateTimePickerComponentView.h b/ios/fabric/RNDateTimePickerComponentView.h
new file mode 100644
index 00000000..9b54089e
--- /dev/null
+++ b/ios/fabric/RNDateTimePickerComponentView.h
@@ -0,0 +1,9 @@
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface RNDateTimePickerComponentView : RCTViewComponentView
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/fabric/RNDateTimePickerComponentView.mm b/ios/fabric/RNDateTimePickerComponentView.mm
new file mode 100644
index 00000000..e3230855
--- /dev/null
+++ b/ios/fabric/RNDateTimePickerComponentView.mm
@@ -0,0 +1,206 @@
+#import "RNDateTimePickerComponentView.h"
+
+#import
+
+#import
+#import
+#import
+#import
+
+#import "RCTFabricComponentsPlugins.h"
+#import "RNDateTimePicker.h"
+
+using namespace facebook::react;
+
+@interface RNDateTimePickerComponentView ()
+@end
+
+@implementation RNDateTimePickerComponentView {
+ UIDatePicker *_datePickerView;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame
+{
+ if (self = [super initWithFrame:frame]) {
+ static const auto defaultProps = std::make_shared();
+ _props = defaultProps;
+
+ _datePickerView = [[RNDateTimePicker alloc] initWithFrame:self.bounds];
+
+ [_datePickerView addTarget:self action:@selector(onChange:) forControlEvents:UIControlEventValueChanged];
+
+ // Default Picker mode
+ _datePickerView.datePickerMode = UIDatePickerModeDate;
+
+ self.contentView = _datePickerView;
+ }
+
+ return self;
+}
+
+-(void)onChange:(RNDateTimePicker *)sender
+{
+ if (!_eventEmitter) {
+ return;
+ }
+
+ NSTimeInterval timestamp = [sender.date timeIntervalSince1970];
+ RNDateTimePickerEventEmitter::OnChange event = {
+ // Sending time in milliseconds
+ .timestamp = timestamp * 1000
+ };
+
+ std::dynamic_pointer_cast(_eventEmitter)
+ ->onChange(event);
+}
+
+#pragma mark - RCTComponentViewProtocol
+
++ (ComponentDescriptorProvider)componentDescriptorProvider
+{
+ return concreteComponentDescriptorProvider();
+}
+
+// JS Standard for time is milliseconds
+NSDate* convertJSTimeToDate (double jsTime) {
+ double time = jsTime/1000.0;
+ return [NSDate dateWithTimeIntervalSince1970: time];
+}
+
+-(void)updateTextColor:(UIColor *)color
+{
+ if (@available(iOS 14.0, *)) {
+ if (_datePickerView.datePickerStyle != UIDatePickerStyleWheels) {
+ // prevents #247
+ return;
+ }
+ }
+
+ if (color == nil) {
+ // Default Text color
+ if (@available(iOS 13.0, *)) {
+ color = [UIColor labelColor];
+ } else {
+ color = [UIColor blackColor];
+ }
+ }
+
+ [_datePickerView setValue:color forKey:@"textColor"];
+ [_datePickerView setValue:@(NO) forKey:@"highlightsToday"];
+}
+
+- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
+{
+ const auto &oldPickerProps = *std::static_pointer_cast(_props);
+ const auto &newPickerProps = *std::static_pointer_cast(props);
+
+ if (oldPickerProps.date != newPickerProps.date) {
+ _datePickerView.date = convertJSTimeToDate(newPickerProps.date);
+ }
+
+ if (oldPickerProps.minimumDate != newPickerProps.minimumDate) {
+ _datePickerView.minimumDate = convertJSTimeToDate(newPickerProps.minimumDate);
+ }
+
+ if (oldPickerProps.maximumDate != newPickerProps.maximumDate) {
+ _datePickerView.maximumDate = convertJSTimeToDate(newPickerProps.maximumDate);
+ }
+
+ if (oldPickerProps.locale != newPickerProps.locale) {
+ NSString *convertedLocale = RCTNSStringFromString(newPickerProps.locale);
+ NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:convertedLocale];
+
+ _datePickerView.locale = locale;
+ }
+
+ if (oldPickerProps.mode != newPickerProps.mode) {
+ switch(newPickerProps.mode) {
+ case RNDateTimePickerMode::Time:
+ _datePickerView.datePickerMode = UIDatePickerModeTime;
+ break;
+ case RNDateTimePickerMode::Datetime:
+ _datePickerView.datePickerMode = UIDatePickerModeDateAndTime;
+ break;
+ case RNDateTimePickerMode::Countdown:
+ _datePickerView.datePickerMode = UIDatePickerModeCountDownTimer;
+ break;
+ default:
+ _datePickerView.datePickerMode = UIDatePickerModeDate;
+ }
+ }
+
+ if (@available(iOS 14.0, *)) {
+ if (oldPickerProps.displayIOS != newPickerProps.displayIOS) {
+ switch(newPickerProps.displayIOS) {
+ case RNDateTimePickerDisplayIOS::Compact:
+ _datePickerView.preferredDatePickerStyle = UIDatePickerStyleCompact;
+ break;
+ case RNDateTimePickerDisplayIOS::Inline:
+ _datePickerView.preferredDatePickerStyle = UIDatePickerStyleInline;
+ break;
+ case RNDateTimePickerDisplayIOS::Spinner:
+ _datePickerView.preferredDatePickerStyle = UIDatePickerStyleWheels;
+ break;
+ default:
+ _datePickerView.preferredDatePickerStyle = UIDatePickerStyleAutomatic;
+ }
+ }
+ }
+
+ if (oldPickerProps.minuteInterval != newPickerProps.minuteInterval) {
+ _datePickerView.minuteInterval = newPickerProps.minuteInterval;
+ }
+
+ if (oldPickerProps.timeZoneOffsetInMinutes != newPickerProps.timeZoneOffsetInMinutes) {
+ // JS standard for time zones is minutes.
+ _datePickerView.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:newPickerProps.timeZoneOffsetInMinutes * 60.0];
+ }
+
+ if (oldPickerProps.accentColor != newPickerProps.accentColor) {
+ UIColor *color = RCTUIColorFromSharedColor(newPickerProps.accentColor);
+
+ if (color != nil) {
+ [_datePickerView setTintColor:color];
+ } else {
+ if (@available(iOS 15.0, *)) {
+ [_datePickerView setTintColor:[UIColor tintColor]];
+ } else {
+ [_datePickerView setTintColor:[UIColor systemBlueColor]];
+ }
+ }
+ }
+
+ if (oldPickerProps.textColor != newPickerProps.textColor) {
+ [self updateTextColor:RCTUIColorFromSharedColor(newPickerProps.textColor)];
+ }
+
+ if (@available(iOS 13.0, *)) {
+ if (oldPickerProps.themeVariant != newPickerProps.themeVariant) {
+ switch (newPickerProps.themeVariant) {
+ case RNDateTimePickerThemeVariant::Light:
+ _datePickerView.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
+ break;
+ case RNDateTimePickerThemeVariant::Dark:
+ _datePickerView.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
+ break;
+ default:
+ _datePickerView.overrideUserInterfaceStyle = UIUserInterfaceStyleUnspecified;
+ }
+ }
+ }
+
+ if (oldPickerProps.enabled != newPickerProps.enabled) {
+ _datePickerView.enabled = newPickerProps.enabled;
+ }
+
+
+ [super updateProps:props oldProps:oldProps];
+}
+
+@end
+
+Class RNDateTimePickerCls(void)
+{
+ return RNDateTimePickerComponentView.class;
+}
+
diff --git a/package.json b/package.json
index bfa156cb..61aaa3ba 100644
--- a/package.json
+++ b/package.json
@@ -144,5 +144,10 @@
}
}
}
+ },
+ "codegenConfig": {
+ "name": "RNDateTimePicker",
+ "type": "components",
+ "jsSrcsDir": "src/specs"
}
}
diff --git a/src/datetimepicker.ios.js b/src/datetimepicker.ios.js
index fed4dfee..44bc56e1 100644
--- a/src/datetimepicker.ios.js
+++ b/src/datetimepicker.ios.js
@@ -10,7 +10,7 @@
* @flow strict-local
*/
import RNDateTimePicker from './picker';
-import {sharedPropsValidation, toMilliseconds} from './utils';
+import {isFabricEnabled, sharedPropsValidation, toMilliseconds} from './utils';
import {IOS_DISPLAY, ANDROID_MODE, EVENT_TYPE_SET} from './constants';
import invariant from 'invariant';
import * as React from 'react';
@@ -18,13 +18,13 @@ import {getPickerHeightStyle} from './layoutUtilsIOS';
import {Platform, StyleSheet} from 'react-native';
import type {
+ DateTimePickerEvent,
NativeEventIOS,
NativeRef,
IOSNativeProps,
DatePickerOptions,
IOSDisplay,
} from './types';
-import type {DateTimePickerEvent} from './types';
const getDisplaySafe = (display: IOSDisplay): IOSDisplay => {
const majorVersionIOS = parseInt(Platform.Version, 10);
@@ -67,6 +67,11 @@ export default function Picker({
React.useEffect(
function ensureNativeIsInSyncWithJS() {
+ if (isFabricEnabled) {
+ // we don't need this workaround when fabric is enabled
+ return;
+ }
+
const {current} = _picker;
if (value && onChange && current) {
@@ -111,7 +116,6 @@ export default function Picker({
const dates: DatePickerOptions = {value, maximumDate, minimumDate};
toMilliseconds(dates, 'value', 'minimumDate', 'maximumDate');
-
return (
// $FlowFixMe - dozen of flow errors
;
+
+type NativeProps = $ReadOnly<{|
+ ...ViewProps,
+ onChange?: BubblingEventHandler,
+ maximumDate?: ?Double,
+ minimumDate?: ?Double,
+ date?: ?Double,
+ locale?: ?string,
+ minuteInterval?: ?Int32,
+ mode?: WithDefault<'date' | 'time' | 'datetime' | 'countdown', 'date'>,
+ timeZoneOffsetInMinutes?: Double,
+ textColor?: ?ColorValue,
+ accentColor?: ?ColorValue,
+ themeVariant?: WithDefault<'dark' | 'light' | 'unspecified', 'unspecified'>,
+ displayIOS?: WithDefault<
+ 'default' | 'spinner' | 'compact' | 'inline',
+ 'default',
+ >,
+ enabled?: WithDefault,
+|}>;
+
+export default (codegenNativeComponent(
+ 'RNDateTimePicker',
+): HostComponent);
diff --git a/src/utils.js b/src/utils.js
index cc7e87b3..f8f124d8 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -30,3 +30,5 @@ export function sharedPropsValidation({value}: {value: ?Date}) {
'`value` prop must be an instance of Date object',
);
}
+
+export const isFabricEnabled = global.nativeFabricUIManager !== null;