├── example ├── .watchmanconfig ├── tsconfig.json ├── babel.config.js ├── android │ ├── debug.keystore │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── app │ │ └── src │ │ │ └── androidTest │ │ │ └── java │ │ │ └── com │ │ │ └── microsoft │ │ │ └── reacttestapp │ │ │ └── DetoxTest.java │ ├── gradle.properties │ ├── gradlew.bat │ └── build.gradle ├── e2e │ ├── test-butler-app.apk │ ├── jest.config.js │ └── utils │ │ ├── utils.js │ │ ├── assertions.js │ │ ├── matchers.js │ │ └── actions.js ├── index.js ├── .gitignore ├── macos │ └── Podfile ├── ios │ └── Podfile ├── SegmentedControl.js ├── visionos │ └── Podfile ├── windows │ └── .gitignore └── app.json ├── ios ├── RNDateTimePicker.xcodeproj │ └── project.pbxproj ├── RNDateTimePicker.h ├── fabric │ ├── RNDateTimePickerComponentView.h │ └── cpp │ │ └── react │ │ └── renderer │ │ └── components │ │ └── RNDateTimePicker │ │ ├── RNDateTimePickerState.cpp │ │ ├── ShadowNodes.cpp │ │ ├── RNDateTimePickerState.h │ │ ├── ShadowNodes.h │ │ └── ComponentDescriptors.h ├── RNDateTimePickerManager.h ├── RNDateTimePickerShadowView.h ├── RNDateTimePicker.m └── RNDateTimePickerShadowView.m ├── app.plugin.js ├── .yarnrc.yml ├── .github ├── FUNDING.yml ├── workflows │ ├── label-actions.yml │ └── publish.yml ├── PULL_REQUEST_TEMPLATE.md └── label-actions.yml ├── docs ├── images │ ├── ios_time.png │ ├── android_date.png │ ├── android_time.png │ ├── ios_date_new.png │ ├── ios_date_old.png │ ├── windows_date.png │ ├── windows_time_1.png │ ├── windows_time_2.png │ ├── android_material_date.jpg │ ├── android_material_time.jpg │ ├── time_picker_breakdown.png │ └── date_picker_dialog_breakdown.png ├── android-styling.md ├── manual-installation.md └── migration.md ├── src ├── materialdatepicker.js ├── materialtimepicker.js ├── index.js ├── DateTimePickerAndroid.js ├── picker.android.js ├── picker.ios.js ├── datetimepicker.js ├── picker.windows.js ├── specs │ ├── NativeModuleTimePicker.js │ ├── NativeModuleMaterialTimePicker.js │ ├── NativeModuleDatePicker.js │ ├── NativeModuleMaterialDatePicker.js │ └── DateTimePickerNativeComponent.js ├── eventCreators.js ├── constants.js ├── utils.js ├── materialtimepicker.android.js ├── timepicker.android.js ├── datepicker.android.js ├── materialdatepicker.android.js ├── datetimepicker.android.js ├── datetimepicker.windows.js ├── datetimepicker.ios.js ├── DateTimePickerAndroid.android.js └── androidUtils.js ├── android ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── reactcommunity │ │ │ │ └── rndatetimepicker │ │ │ │ ├── RNMaterialInputMode.java │ │ │ │ ├── RNDatePickerDisplay.java │ │ │ │ ├── RNTimePickerDisplay.java │ │ │ │ ├── RNDate.java │ │ │ │ ├── ReflectionHelper.java │ │ │ │ ├── MaterialDatePickerModule.kt │ │ │ │ ├── RNConstants.java │ │ │ │ ├── MaterialTimePickerModule.kt │ │ │ │ ├── RNDateTimePickerPackage.java │ │ │ │ ├── RNTimePickerDialogFragment.java │ │ │ │ ├── RNDismissableTimePickerDialog.java │ │ │ │ ├── RNDismissableDatePickerDialog.java │ │ │ │ ├── RNMaterialTimePicker.kt │ │ │ │ ├── TimePickerModule.java │ │ │ │ └── DatePickerModule.java │ │ └── res │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ └── paper │ │ └── java │ │ └── com │ │ └── reactcommunity │ │ └── rndatetimepicker │ │ ├── NativeModuleDatePickerSpec.java │ │ ├── NativeModuleTimePickerSpec.java │ │ ├── NativeModuleMaterialDatePickerSpec.java │ │ └── NativeModuleMaterialTimePickerSpec.java └── build.gradle ├── windows └── DateTimePickerWindows │ ├── pch.cpp │ ├── DateTimePicker.rc │ ├── DateTimePickerWindows.def │ ├── packages.config │ ├── DateTimePickerWindows_TemporaryKey.pfx │ ├── pch.h │ ├── ReactPackageProvider.idl │ ├── resource.h │ ├── PropertySheet.props │ ├── DateTimePickerView.idl │ ├── ReactPackageProvider.h │ ├── ReactPackageProvider.cpp │ ├── TimePickerView.h │ ├── DateTimePickerView.h │ ├── DateTimePickerWindows.vcxproj.filters │ ├── TimePickerViewManager.h │ ├── DateTimePickerViewManager.h │ ├── TimePickerViewManager.cpp │ ├── DateTimePickerViewManager.cpp │ └── TimePickerView.cpp ├── .prettierrc ├── jest.config.js ├── plugin └── tsconfig.json ├── flow-typed └── npm │ └── invariant_v2.x.x.js ├── .editorconfig ├── babel.config.js ├── .eslintrc.js ├── metro.config.js ├── .releaserc ├── patches └── react-native-test-app+4.4.5.patch ├── RNDateTimePicker.podspec ├── test ├── __snapshots__ │ └── index.test.js.snap ├── utils.test.js ├── userlandTestExamples.test.js └── index.test.js ├── .gitignore ├── LICENSE.md ├── jest └── index.js ├── react-native.config.js ├── .flowconfig ├── CONTRIBUTING.md ├── .detoxrc.js └── package.json /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /ios/RNDateTimePicker.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app.plugin.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./plugin/build/withDateTimePickerStyles'); 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | yarnPath: .yarn/releases/yarn-4.9.2.cjs 4 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@react-native/typescript-config/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [react-native-datetimepicker] 2 | open_collective: react-native-datetimepicker 3 | -------------------------------------------------------------------------------- /docs/images/ios_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/docs/images/ios_time.png -------------------------------------------------------------------------------- /docs/images/android_date.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/docs/images/android_date.png -------------------------------------------------------------------------------- /docs/images/android_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/docs/images/android_time.png -------------------------------------------------------------------------------- /docs/images/ios_date_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/docs/images/ios_date_new.png -------------------------------------------------------------------------------- /docs/images/ios_date_old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/docs/images/ios_date_old.png -------------------------------------------------------------------------------- /docs/images/windows_date.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/docs/images/windows_date.png -------------------------------------------------------------------------------- /docs/images/windows_time_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/docs/images/windows_time_1.png -------------------------------------------------------------------------------- /docs/images/windows_time_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/docs/images/windows_time_2.png -------------------------------------------------------------------------------- /example/android/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/example/android/debug.keystore -------------------------------------------------------------------------------- /src/materialdatepicker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @flow strict-local 4 | */ 5 | 6 | export default class MaterialDatePicker {} 7 | -------------------------------------------------------------------------------- /src/materialtimepicker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @flow strict-local 4 | */ 5 | 6 | export default class MaterialTimePicker {} 7 | -------------------------------------------------------------------------------- /example/e2e/test-butler-app.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/example/e2e/test-butler-app.apk -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/images/android_material_date.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/docs/images/android_material_date.jpg -------------------------------------------------------------------------------- /docs/images/android_material_time.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/docs/images/android_material_time.jpg -------------------------------------------------------------------------------- /docs/images/time_picker_breakdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/docs/images/time_picker_breakdown.png -------------------------------------------------------------------------------- /docs/images/date_picker_dialog_breakdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/docs/images/date_picker_dialog_breakdown.png -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/pch.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | #include "pch.h" 5 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/DateTimePicker.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/windows/DateTimePickerWindows/DateTimePicker.rc -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "requirePragma": false, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "bracketSpacing": false, 6 | "jsxBracketSameLine": true 7 | } 8 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'react-native', 3 | modulePathIgnorePatterns: ['/example/node_modules'], 4 | testRegex: '(/__tests__/.*|(\\.|/)(test))\\.[jt]sx?$', 5 | }; 6 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/DateTimePickerWindows.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE 3 | DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE 4 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactcommunity/rndatetimepicker/RNMaterialInputMode.java: -------------------------------------------------------------------------------- 1 | package com.reactcommunity.rndatetimepicker; 2 | 3 | public enum RNMaterialInputMode { 4 | DEFAULT, 5 | KEYBOARD 6 | } 7 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/DateTimePickerWindows_TemporaryKey.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-datetimepicker/datetimepicker/HEAD/windows/DateTimePickerWindows/DateTimePickerWindows_TemporaryKey.pfx -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/pch.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #define NOMINMAX 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './App'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | *.binlog 2 | *.hprof 3 | *.xcworkspace/ 4 | *.zip 5 | .DS_Store 6 | .gradle/ 7 | .idea/ 8 | .vs/ 9 | .xcode.env 10 | Pods/ 11 | build/ 12 | dist/* 13 | !dist/.gitignore 14 | local.properties 15 | msbuild.binlog 16 | node_modules/ 17 | -------------------------------------------------------------------------------- /plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo-module-scripts/tsconfig.plugin", 3 | "compilerOptions": { 4 | "outDir": "build", 5 | "rootDir": "src", 6 | }, 7 | "include": ["./src"], 8 | "exclude": ["**/__mocks__/*", "**/__tests__/*"] 9 | } 10 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @flow strict-local 4 | */ 5 | import RNDateTimePicker from './datetimepicker'; 6 | export * from './eventCreators'; 7 | export {DateTimePickerAndroid} from './DateTimePickerAndroid'; 8 | 9 | export default RNDateTimePicker; 10 | -------------------------------------------------------------------------------- /flow-typed/npm/invariant_v2.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 60de437d85342dea19dcd82c5a50f88a 2 | // flow-typed version: da30fe6876/invariant_v2.x.x/flow_>=v0.33.x 3 | 4 | declare module invariant { 5 | declare module.exports: (condition: boolean, message: string) => void; 6 | } 7 | -------------------------------------------------------------------------------- /src/DateTimePickerAndroid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @flow strict-local 4 | */ 5 | import {Platform} from 'react-native'; 6 | 7 | const warn = () => { 8 | console.warn(`DateTimePickerAndroid is not supported on: ${Platform.OS}`); 9 | }; 10 | 11 | export const DateTimePickerAndroid = {open: warn, dismiss: warn}; 12 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /ios/RNDateTimePicker.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | @interface RNDateTimePicker : UIDatePicker 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /ios/fabric/RNDateTimePickerComponentView.h: -------------------------------------------------------------------------------- 1 | /** 2 | * RNDateTimePickerComponentView is only be available when fabric is enabled. 3 | */ 4 | 5 | #import 6 | 7 | NS_ASSUME_NONNULL_BEGIN 8 | 9 | @interface RNDateTimePickerComponentView : RCTViewComponentView 10 | 11 | @end 12 | 13 | NS_ASSUME_NONNULL_END 14 | -------------------------------------------------------------------------------- /example/e2e/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | maxWorkers: 1, 3 | testTimeout: 120000, 4 | verbose: true, 5 | reporters: ['detox/runners/jest/reporter'], 6 | globalSetup: 'detox/runners/jest/globalSetup', 7 | globalTeardown: 'detox/runners/jest/globalTeardown', 8 | testEnvironment: 'detox/runners/jest/testEnvironment', 9 | }; 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | -------------------------------------------------------------------------------- /example/macos/Podfile: -------------------------------------------------------------------------------- 1 | ws_dir = Pathname.new(__dir__) 2 | ws_dir = ws_dir.parent until 3 | File.exist?("#{ws_dir}/node_modules/react-native-test-app/macos/test_app.rb") || 4 | ws_dir.expand_path.to_s == '/' 5 | require "#{ws_dir}/node_modules/react-native-test-app/macos/test_app.rb" 6 | 7 | workspace 'date-time-picker-example.xcworkspace' 8 | 9 | use_test_app! 10 | -------------------------------------------------------------------------------- /ios/fabric/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom state to store frameSize that the component descriptor will use to modify the 3 | * shadow node layout. 4 | */ 5 | 6 | #include "RNDateTimePickerState.h" 7 | 8 | namespace facebook { 9 | namespace react { 10 | 11 | } // namespace react 12 | } // namespace facebook -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const isLintingOrTesting = 2 | process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'lint'; 3 | 4 | module.exports = { 5 | presets: [ 6 | [ 7 | 'module:@react-native/babel-preset', 8 | // this is a workaround for some deeper issue 9 | {useTransformReactJSXExperimental: !isLintingOrTesting}, 10 | ], 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /src/picker.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @flow strict-local 4 | */ 5 | import DatePickerAndroid from './datepicker'; 6 | import TimePickerAndroid from './timepicker'; 7 | import {ANDROID_MODE} from './constants'; 8 | 9 | const pickers = { 10 | [ANDROID_MODE.date]: DatePickerAndroid, 11 | [ANDROID_MODE.time]: TimePickerAndroid, 12 | }; 13 | 14 | export default pickers; 15 | -------------------------------------------------------------------------------- /src/picker.ios.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @format 8 | * @flow strict-local 9 | */ 10 | import RNDateTimePicker from './specs/DateTimePickerNativeComponent'; 11 | 12 | export default RNDateTimePicker; 13 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/ReactPackageProvider.idl: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | namespace DateTimePicker 5 | { 6 | [webhosthidden] 7 | [default_interface] 8 | runtimeclass ReactPackageProvider : Microsoft.ReactNative.IReactPackageProvider 9 | { 10 | ReactPackageProvider(); 11 | }; 12 | } -------------------------------------------------------------------------------- /src/datetimepicker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @flow strict-local 4 | */ 5 | 6 | import React from 'react'; 7 | import {Platform} from 'react-native'; 8 | 9 | import type {BaseProps} from './types'; 10 | 11 | export default function DateTimePicker(_props: BaseProps): null { 12 | React.useEffect(() => { 13 | console.warn(`DateTimePicker is not supported on: ${Platform.OS}`); 14 | }, []); 15 | return null; 16 | } 17 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | ws_dir = Pathname.new(__dir__) 2 | ws_dir = ws_dir.parent until 3 | File.exist?("#{ws_dir}/node_modules/react-native-test-app/test_app.rb") || 4 | ws_dir.expand_path.to_s == '/' 5 | require "#{ws_dir}/node_modules/react-native-test-app/test_app.rb" 6 | 7 | workspace 'date-time-picker-example.xcworkspace' 8 | 9 | use_test_app! :hermes_enabled => true, :fabric_enabled => true, :bridgeless_enabled => true 10 | -------------------------------------------------------------------------------- /example/SegmentedControl.js: -------------------------------------------------------------------------------- 1 | import SegmentedControl from '@react-native-segmented-control/segmented-control'; 2 | import JSSegmentedControl from '@react-native-segmented-control/segmented-control/js/SegmentedControl.js'; 3 | 4 | const isFabricEnabled = global.nativeFabricUIManager !== null; 5 | 6 | // Forcing the JS implementation for Fabric as the native module is not compatible with Fabric yet. 7 | export default isFabricEnabled ? JSSegmentedControl : SegmentedControl; 8 | -------------------------------------------------------------------------------- /.github/workflows/label-actions.yml: -------------------------------------------------------------------------------- 1 | name: 'Label Actions' 2 | 3 | on: 4 | issues: 5 | types: [labeled, unlabeled] 6 | # pull_request: 7 | # types: [labeled, unlabeled] 8 | # discussion: 9 | # types: [labeled, unlabeled] 10 | 11 | permissions: 12 | contents: read 13 | issues: write 14 | pull-requests: write 15 | discussions: write 16 | 17 | jobs: 18 | action: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: dessant/label-actions@v2 22 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDisplay.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | package com.reactcommunity.rndatetimepicker; 9 | 10 | /** 11 | * Date picker display options. 12 | */ 13 | public enum RNDatePickerDisplay { 14 | SPINNER, 15 | DEFAULT 16 | } 17 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by DateTimePicker.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactcommunity/rndatetimepicker/RNTimePickerDisplay.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | *

4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | *

7 | */ 8 | 9 | package com.reactcommunity.rndatetimepicker; 10 | 11 | /** 12 | * Date picker display options. 13 | */ 14 | public enum RNTimePickerDisplay { 15 | SPINNER, 16 | DEFAULT 17 | } 18 | -------------------------------------------------------------------------------- /example/visionos/Podfile: -------------------------------------------------------------------------------- 1 | ws_dir = Pathname.new(__dir__) 2 | ws_dir = ws_dir.parent until 3 | File.exist?("#{ws_dir}/node_modules/react-native-test-app/visionos/test_app.rb") || 4 | ws_dir.expand_path.to_s == '/' 5 | require "#{ws_dir}/node_modules/react-native-test-app/visionos/test_app.rb" 6 | 7 | workspace 'date-time-picker-example.xcworkspace' 8 | 9 | use_test_app! do |target| 10 | target.app do 11 | pod 'RNDateTimePicker', :path => '../../RNDateTimePicker.podspec' 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /ios/RNDateTimePickerManager.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | @interface RCTConvert(UIDatePicker) 12 | 13 | + (UIDatePickerMode)UIDatePickerMode:(id)json; 14 | 15 | @end 16 | 17 | @interface RNDateTimePickerManager : RCTViewManager 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /android/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | 3 | # User-specific files 4 | *.suo 5 | *.user 6 | *.sln.docstates 7 | 8 | # Build results 9 | ARM64/ 10 | AppPackages/ 11 | [Bb]in/ 12 | [Dd]ebug/ 13 | [Dd]ebugPublic/ 14 | [Oo]bj/ 15 | [Rr]elease/ 16 | [Rr]eleases/ 17 | bld/ 18 | build/ 19 | x64/ 20 | x86/ 21 | 22 | # NuGet Packages Directory 23 | packages/ 24 | 25 | **/Generated Files/** 26 | *.binlog 27 | *.hprof 28 | *.sln 29 | ExperimentalFeatures.props 30 | NuGet.Config 31 | dist/ 32 | msbuild.binlog 33 | node_modules/ 34 | -------------------------------------------------------------------------------- /src/picker.windows.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @format 8 | * @flow strict-local 9 | */ 10 | import {requireNativeComponent} from 'react-native'; 11 | 12 | export default (requireNativeComponent( 13 | 'RNDateTimePickerWindows', 14 | // $FlowFixMe Flow: Unclear type. Using `any`, `Object`, or `Function` types is not safe! 15 | ): any); 16 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'hermes-eslint', 4 | extends: ['@react-native', 'plugin:jest/recommended'], 5 | ignorePatterns: ['**/*.d.ts'], 6 | globals: { 7 | expect: true, 8 | element: true, 9 | by: true, 10 | device: true, 11 | beforeAll: true, 12 | beforeEach: true, 13 | afterAll: true, 14 | jest: true, 15 | jasmine: true, 16 | waitFor: true, 17 | detoxCircus: true, 18 | }, 19 | rules: { 20 | 'no-var': 2, 21 | 'jest/no-conditional-expect': 0, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const { makeMetroConfig } = require('@rnx-kit/metro-config'); 4 | module.exports = makeMetroConfig({ 5 | projectRoot: path.join(__dirname, 'example'), 6 | watchFolders: [__dirname], 7 | resolver: { 8 | extraNodeModules: { 9 | '@react-native-community/datetimepicker': __dirname, 10 | }, 11 | }, 12 | transformer: { 13 | getTransformOptions: async () => ({ 14 | transform: { 15 | experimentalImportSupport: false, 16 | inlineRequires: false, 17 | }, 18 | }), 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /ios/fabric/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen) 4 | * and copied to the cpp directory to add custom state and set shadow node trait as a LeafYogaNode. 5 | * 6 | * @generated by codegen project: GenerateShadowNodeCpp.js 7 | */ 8 | 9 | #include "ShadowNodes.h" 10 | 11 | namespace facebook { 12 | namespace react { 13 | 14 | extern const char RNDateTimePickerComponentName[] = "RNDateTimePicker"; 15 | 16 | } // namespace react 17 | } // namespace facebook 18 | -------------------------------------------------------------------------------- /ios/RNDateTimePickerShadowView.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "RNDateTimePicker.h" 4 | 5 | @interface RNDateTimePickerShadowView : RCTShadowView 6 | 7 | @property (nullable, nonatomic, strong) RNDateTimePicker *picker; 8 | @property (nonatomic) UIDatePickerMode mode; 9 | @property (nullable, nonatomic, strong) NSDate *date; 10 | @property (nullable, nonatomic, strong) NSLocale *locale; 11 | @property (nonatomic, assign) NSInteger timeZoneOffsetInMinutes; 12 | @property (nullable, nonatomic, strong) NSString *timeZoneName; 13 | @property (nonatomic, assign) UIDatePickerStyle displayIOS API_AVAILABLE(ios(13.4)); 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | mavenCentral() 5 | google() 6 | } 7 | } 8 | 9 | rootProject.name = "date-time-picker-example" 10 | 11 | apply(from: { 12 | def searchDir = rootDir.toPath() 13 | do { 14 | def p = searchDir.resolve("node_modules/react-native-test-app/test-app.gradle") 15 | if (p.toFile().exists()) { 16 | return p.toRealPath().toString() 17 | } 18 | } while (searchDir = searchDir.getParent()) 19 | throw new GradleException("Could not find `react-native-test-app`"); 20 | }()) 21 | applyTestAppSettings(settings) 22 | -------------------------------------------------------------------------------- /example/e2e/utils/utils.js: -------------------------------------------------------------------------------- 1 | const isAndroid = () => device.getPlatform() === 'android'; 2 | const isIOS = () => device.getPlatform() === 'ios'; 3 | const wait = async (time = 1000) => 4 | new Promise((resolve) => setTimeout(resolve, time)); 5 | 6 | const Platform = { 7 | select: (objectWithPlatformKeys) => { 8 | const platform = device.getPlatform(); 9 | if (typeof objectWithPlatformKeys[platform] === 'function') { 10 | return objectWithPlatformKeys[platform](); 11 | } else { 12 | return objectWithPlatformKeys[platform]; 13 | } 14 | }, 15 | }; 16 | 17 | module.exports = { 18 | isAndroid, 19 | isIOS, 20 | wait, 21 | Platform, 22 | }; 23 | -------------------------------------------------------------------------------- /ios/fabric/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom state to store frameSize that the component descriptor will use to modify the 3 | * shadow node layout. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | namespace facebook { 11 | namespace react { 12 | 13 | class RNDateTimePickerState final { 14 | public: 15 | using Shared = std::shared_ptr; 16 | RNDateTimePickerState(){}; 17 | RNDateTimePickerState(Size frameSize_) : frameSize(frameSize_){}; 18 | 19 | Size frameSize{}; 20 | }; 21 | 22 | } // namespace react 23 | } // namespace facebook 24 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/PropertySheet.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@semantic-release/commit-analyzer", 4 | "@semantic-release/release-notes-generator", 5 | "@semantic-release/npm", 6 | [ 7 | "@semantic-release/github", 8 | { 9 | "successComment": ":tada: This issue has been resolved in version ${nextRelease.version} :tada:\n\nIf this package helps you, consider [sponsoring us](https://github.com/sponsors/react-native-datetimepicker)! :rocket:", 10 | "addReleases": "bottom" 11 | } 12 | ], 13 | [ 14 | "@semantic-release/git", 15 | { 16 | "assets": "package.json", 17 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 18 | } 19 | ] 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /example/e2e/utils/assertions.js: -------------------------------------------------------------------------------- 1 | const {elementById} = require('./matchers'); 2 | 3 | async function assertTimeLabels({utcTime, deviceTime, overriddenTime}) { 4 | await expect(elementById('utcTime')).toHaveText(utcTime); 5 | await expect(elementById('deviceTime')).toHaveText(deviceTime); 6 | await expect(elementById('overriddenTime')).toHaveText( 7 | overriddenTime ?? deviceTime, 8 | ); 9 | } 10 | 11 | async function assertInitialTimeLabels() { 12 | return await assertTimeLabels({ 13 | utcTime: '2021-11-13T01:00:00Z', 14 | deviceTime: '2021-11-13T02:00:00+01:00', 15 | overriddenTime: '2021-11-13T02:00:00+01:00', 16 | }); 17 | } 18 | module.exports = {assertTimeLabels, assertInitialTimeLabels}; 19 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/DateTimePickerView.idl: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | namespace DateTimePicker { 5 | 6 | [default_interface] 7 | runtimeclass DateTimePickerView : Windows.UI.Xaml.Controls.CalendarDatePicker { 8 | DateTimePickerView(Microsoft.ReactNative.IReactContext context); 9 | void UpdateProperties(Microsoft.ReactNative.IJSValueReader reader); 10 | }; 11 | 12 | [default_interface] 13 | runtimeclass TimePickerView : Windows.UI.Xaml.Controls.TimePicker { 14 | TimePickerView(Microsoft.ReactNative.IReactContext context); 15 | void UpdateProperties(Microsoft.ReactNative.IJSValueReader reader); 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/ReactPackageProvider.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include "ReactPackageProvider.g.h" 7 | 8 | using namespace winrt::Microsoft::ReactNative; 9 | 10 | namespace winrt::DateTimePicker::implementation 11 | { 12 | struct ReactPackageProvider : ReactPackageProviderT 13 | { 14 | ReactPackageProvider() = default; 15 | 16 | void CreatePackage(IReactPackageBuilder const& packageBuilder) noexcept; 17 | }; 18 | } 19 | 20 | namespace winrt::DateTimePicker::factory_implementation 21 | { 22 | struct ReactPackageProvider : ReactPackageProviderT {}; 23 | } -------------------------------------------------------------------------------- /example/e2e/utils/matchers.js: -------------------------------------------------------------------------------- 1 | const elementById = (id) => element(by.id(id)); 2 | const elementByText = (text) => element(by.text(text)); 3 | 4 | const getDateTimePickerIOS = () => 5 | element(by.type('UIPickerView').withAncestor(by.id('dateTimePicker'))); 6 | 7 | const getInlineTimePickerIOS = () => element(by.label('Time Picker')); 8 | 9 | const getDateTimePickerControlIOS = () => element(by.type('UIDatePicker')); 10 | 11 | const getDatePickerAndroid = () => element(by.id('dateTimePicker')); 12 | 13 | const getDatePickerButtonIOS = () => element(by.id('dateTimePicker')); 14 | 15 | module.exports = { 16 | elementById, 17 | elementByText, 18 | getDateTimePickerIOS, 19 | getDateTimePickerControlIOS, 20 | getDatePickerAndroid, 21 | getInlineTimePickerIOS, 22 | getDatePickerButtonIOS, 23 | }; 24 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/ReactPackageProvider.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | #include "pch.h" 5 | #include "ReactPackageProvider.h" 6 | #include "ReactPackageProvider.g.cpp" 7 | 8 | #include "DateTimePickerViewManager.h" 9 | #include "TimePickerViewManager.h" 10 | 11 | using namespace winrt::Microsoft::ReactNative; 12 | 13 | namespace winrt::DateTimePicker::implementation { 14 | 15 | void ReactPackageProvider::CreatePackage(IReactPackageBuilder const& packageBuilder) noexcept { 16 | packageBuilder.AddViewManager(L"DateTimePickerViewManager", []() { return winrt::make(); }); 17 | packageBuilder.AddViewManager(L"TimePickerViewManager", []() { return winrt::make(); }); 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /patches/react-native-test-app+4.4.5.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/react-native-test-app/android/app/src/main/res/values/styles.xml b/node_modules/react-native-test-app/android/app/src/main/res/values/styles.xml 2 | index e58cbf3..ba878cb 100644 3 | --- a/node_modules/react-native-test-app/android/app/src/main/res/values/styles.xml 4 | +++ b/node_modules/react-native-test-app/android/app/src/main/res/values/styles.xml 5 | @@ -1,7 +1,7 @@ 6 | 7 | 8 | 9 | - 5 | 10 | 11 | 14 | 19 | 20 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 React Native Community 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/eventCreators.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow strict-local 3 | */ 4 | import type {DateTimePickerEvent} from './types'; 5 | import {ANDROID_EVT_TYPE, EVENT_TYPE_SET} from './constants'; 6 | 7 | export const createDateTimeSetEvtParams = ( 8 | date: Date, 9 | utcOffset: number, 10 | ): [DateTimePickerEvent, Date] => { 11 | return [ 12 | { 13 | type: EVENT_TYPE_SET, 14 | nativeEvent: { 15 | timestamp: date.getTime(), 16 | utcOffset, 17 | }, 18 | }, 19 | date, 20 | ]; 21 | }; 22 | 23 | export const createDismissEvtParams = ( 24 | date: Date, 25 | utcOffset: number, 26 | ): [DateTimePickerEvent, Date] => { 27 | return [ 28 | { 29 | type: ANDROID_EVT_TYPE.dismissed, 30 | nativeEvent: { 31 | timestamp: date.getTime(), 32 | utcOffset, 33 | }, 34 | }, 35 | date, 36 | ]; 37 | }; 38 | 39 | export const createNeutralEvtParams = ( 40 | date: Date, 41 | utcOffset: number, 42 | ): [DateTimePickerEvent, Date] => { 43 | return [ 44 | { 45 | type: ANDROID_EVT_TYPE.neutralButtonPressed, 46 | nativeEvent: { 47 | timestamp: date.getTime(), 48 | utcOffset, 49 | }, 50 | }, 51 | date, 52 | ]; 53 | }; 54 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: release-please 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | permissions: 9 | id-token: write # Required for kOIDC 10 | contents: write 11 | pull-requests: write 12 | issues: write 13 | 14 | jobs: 15 | release-please: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: googleapis/release-please-action@v4 19 | id: release 20 | with: 21 | release-type: node 22 | # The logic below handles the npm publication: 23 | - uses: actions/checkout@v4 24 | # these if statements ensure that a publication only occurs when 25 | # a new release is created: 26 | if: ${{ steps.release.outputs.release_created }} 27 | - uses: actions/setup-node@v4 28 | with: 29 | node-version: 22 30 | registry-url: 'https://registry.npmjs.org' 31 | if: ${{ steps.release.outputs.release_created }} 32 | - run: | 33 | yarn install --immutable 34 | yarn plugin:build 35 | if: ${{ steps.release.outputs.release_created }} 36 | - run: | 37 | npm install -g npm@latest 38 | npm publish 39 | if: ${{ steps.release.outputs.release_created }} 40 | -------------------------------------------------------------------------------- /example/android/app/src/androidTest/java/com/microsoft/reacttestapp/DetoxTest.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.reacttestapp; 2 | 3 | import com.wix.detox.Detox; 4 | import com.wix.detox.config.DetoxConfig; 5 | 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import androidx.test.ext.junit.runners.AndroidJUnit4; 11 | import androidx.test.filters.LargeTest; 12 | import androidx.test.rule.ActivityTestRule; 13 | 14 | @RunWith(AndroidJUnit4.class) 15 | @LargeTest 16 | public class DetoxTest { 17 | @Rule 18 | // Replace 'MainActivity' with the value of android:name entry in 19 | // in AndroidManifest.xml 20 | public ActivityTestRule mActivityRule = new ActivityTestRule<>(com.microsoft.reacttestapp.MainActivity.class, false, false); 21 | 22 | @Test 23 | public void runDetoxTests() { 24 | DetoxConfig detoxConfig = new DetoxConfig(); 25 | detoxConfig.idlePolicyConfig.masterTimeoutSec = 90; 26 | detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60; 27 | detoxConfig.rnContextLoadTimeoutSec = (com.microsoft.reacttestapp.BuildConfig.DEBUG ? 180 : 60); 28 | 29 | Detox.runTests(mActivityRule, detoxConfig); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/TimePickerView.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include "TimePickerView.g.h" 7 | #include "winrt/Microsoft.ReactNative.h" 8 | #include "NativeModules.h" 9 | 10 | namespace winrt::DateTimePicker::implementation { 11 | 12 | namespace xaml = winrt::Windows::UI::Xaml; 13 | 14 | class TimePickerView : public TimePickerViewT { 15 | public: 16 | TimePickerView(Microsoft::ReactNative::IReactContext const& reactContext); 17 | void UpdateProperties(Microsoft::ReactNative::IJSValueReader const& reader); 18 | 19 | private: 20 | Microsoft::ReactNative::IReactContext m_reactContext{ nullptr }; 21 | bool m_updating{ false }; 22 | xaml::Controls::TimePicker::SelectedTimeChanged_revoker m_timePickerSelectedTimeChangedRevoker{}; 23 | 24 | void RegisterEvents(); 25 | void OnTimeChanged(winrt::Windows::Foundation::IInspectable const& sender, xaml::Controls::TimePickerSelectedValueChangedEventArgs const& args); 26 | 27 | int64_t m_selectedTime; 28 | }; 29 | } 30 | 31 | namespace winrt::DateTimePicker::factory_implementation { 32 | struct TimePickerView : TimePickerViewT {}; 33 | } -------------------------------------------------------------------------------- /jest/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @flow strict-local 4 | */ 5 | import * as androidUtils from '../src/androidUtils'; 6 | import {DATE_SET_ACTION, DISMISS_ACTION} from '../src/constants'; 7 | import type {PresentPickerCallback} from '../src/androidUtils'; 8 | 9 | export const mockAndroidDialogDateChange = (datePickedByUser: Date) => { 10 | jest.spyOn(androidUtils, 'getOpenPicker').mockImplementation(() => { 11 | // $FlowFixMe[missing-local-annot] 12 | async function fakeDateTimePickerAndroidOpener({ 13 | value: timestampFromPickerValueProp, 14 | }) { 15 | const pickedDate = new Date(timestampFromPickerValueProp); 16 | pickedDate.setTime(datePickedByUser.getTime()); 17 | 18 | return { 19 | action: DATE_SET_ACTION, 20 | timestamp: pickedDate.getTime(), 21 | utcOffset: 0, 22 | }; 23 | } 24 | return (fakeDateTimePickerAndroidOpener: PresentPickerCallback); 25 | }); 26 | }; 27 | 28 | export const mockAndroidDialogDismissal = () => { 29 | jest.spyOn(androidUtils, 'getOpenPicker').mockImplementation(() => { 30 | async function fakeDateTimePickerAndroidOpener() { 31 | return { 32 | action: DISMISS_ACTION, 33 | }; 34 | } 35 | // $FlowExpectedError - the typings actually don't 100% reflect the native module behavior 36 | return (fakeDateTimePickerAndroidOpener: PresentPickerCallback); 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /ios/fabric/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen) 4 | * and copied to the cpp directory to add custom state and set shadow node trait as a LeafYogaNode. 5 | * 6 | * @generated by codegen project: GenerateShadowNodeH.js 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "RNDateTimePickerState.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace facebook { 19 | namespace react { 20 | 21 | JSI_EXPORT extern const char RNDateTimePickerComponentName[]; 22 | 23 | /* 24 | * `ShadowNode` for component. 25 | */ 26 | class JSI_EXPORT RNDateTimePickerShadowNode final : public ConcreteViewShadowNode { 27 | 28 | public: 29 | using ConcreteViewShadowNode::ConcreteViewShadowNode; 30 | 31 | static ShadowNodeTraits BaseTraits() { 32 | auto traits = ConcreteViewShadowNode::BaseTraits(); 33 | traits.set(ShadowNodeTraits::Trait::LeafYogaNode); 34 | return traits; 35 | } 36 | }; 37 | 38 | } // namespace react 39 | } // namespace facebook 40 | -------------------------------------------------------------------------------- /android/src/paper/java/com/reactcommunity/rndatetimepicker/NativeModuleDatePickerSpec.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 4 | * 5 | * Then it was commited. It is here to support the old architecture. 6 | * If you use the new architecture, this file won't be included and instead will be generated by the codegen. 7 | * 8 | * @generated by codegen project: GenerateModuleJavaSpec.js 9 | * 10 | * @nolint 11 | */ 12 | 13 | package com.reactcommunity.rndatetimepicker; 14 | 15 | import com.facebook.proguard.annotations.DoNotStrip; 16 | import com.facebook.react.bridge.Promise; 17 | import com.facebook.react.bridge.ReactApplicationContext; 18 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 19 | import com.facebook.react.bridge.ReactMethod; 20 | import com.facebook.react.bridge.ReactModuleWithSpec; 21 | import com.facebook.react.bridge.ReadableMap; 22 | import com.facebook.react.turbomodule.core.interfaces.TurboModule; 23 | 24 | public abstract class NativeModuleDatePickerSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule { 25 | public NativeModuleDatePickerSpec(ReactApplicationContext reactContext) { 26 | super(reactContext); 27 | } 28 | 29 | @ReactMethod 30 | @DoNotStrip 31 | public abstract void dismiss(Promise promise); 32 | 33 | @ReactMethod 34 | @DoNotStrip 35 | public abstract void open(ReadableMap params, Promise promise); 36 | } 37 | -------------------------------------------------------------------------------- /android/src/paper/java/com/reactcommunity/rndatetimepicker/NativeModuleTimePickerSpec.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 4 | * 5 | * Then it was commited. It is here to support the old architecture. 6 | * If you use the new architecture, this file won't be included and instead will be generated by the codegen. 7 | * 8 | * @generated by codegen project: GenerateModuleJavaSpec.js 9 | * 10 | * @nolint 11 | */ 12 | 13 | package com.reactcommunity.rndatetimepicker; 14 | 15 | import com.facebook.proguard.annotations.DoNotStrip; 16 | import com.facebook.react.bridge.Promise; 17 | import com.facebook.react.bridge.ReactApplicationContext; 18 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 19 | import com.facebook.react.bridge.ReactMethod; 20 | import com.facebook.react.bridge.ReactModuleWithSpec; 21 | import com.facebook.react.bridge.ReadableMap; 22 | import com.facebook.react.turbomodule.core.interfaces.TurboModule; 23 | 24 | public abstract class NativeModuleTimePickerSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule { 25 | public NativeModuleTimePickerSpec(ReactApplicationContext reactContext) { 26 | super(reactContext); 27 | } 28 | 29 | @ReactMethod 30 | @DoNotStrip 31 | public abstract void dismiss(Promise promise); 32 | 33 | @ReactMethod 34 | @DoNotStrip 35 | public abstract void open(ReadableMap params, Promise promise); 36 | } 37 | -------------------------------------------------------------------------------- /android/src/paper/java/com/reactcommunity/rndatetimepicker/NativeModuleMaterialDatePickerSpec.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 4 | * 5 | * Then it was commited. It is here to support the old architecture. 6 | * If you use the new architecture, this file won't be included and instead will be generated by the codegen. 7 | * 8 | * @generated by codegen project: GenerateModuleJavaSpec.js 9 | * 10 | * @nolint 11 | */ 12 | 13 | package com.reactcommunity.rndatetimepicker; 14 | 15 | import com.facebook.proguard.annotations.DoNotStrip; 16 | import com.facebook.react.bridge.Promise; 17 | import com.facebook.react.bridge.ReactApplicationContext; 18 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 19 | import com.facebook.react.bridge.ReactMethod; 20 | import com.facebook.react.bridge.ReactModuleWithSpec; 21 | import com.facebook.react.bridge.ReadableMap; 22 | import com.facebook.react.turbomodule.core.interfaces.TurboModule; 23 | 24 | public abstract class NativeModuleMaterialDatePickerSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule { 25 | public NativeModuleMaterialDatePickerSpec(ReactApplicationContext reactContext) { 26 | super(reactContext); 27 | } 28 | 29 | @ReactMethod 30 | @DoNotStrip 31 | public abstract void dismiss(Promise promise); 32 | 33 | @ReactMethod 34 | @DoNotStrip 35 | public abstract void open(ReadableMap params, Promise promise); 36 | } 37 | -------------------------------------------------------------------------------- /android/src/paper/java/com/reactcommunity/rndatetimepicker/NativeModuleMaterialTimePickerSpec.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 4 | * 5 | * Then it was commited. It is here to support the old architecture. 6 | * If you use the new architecture, this file won't be included and instead will be generated by the codegen. 7 | * 8 | * @generated by codegen project: GenerateModuleJavaSpec.js 9 | * 10 | * @nolint 11 | */ 12 | 13 | package com.reactcommunity.rndatetimepicker; 14 | 15 | import com.facebook.proguard.annotations.DoNotStrip; 16 | import com.facebook.react.bridge.Promise; 17 | import com.facebook.react.bridge.ReactApplicationContext; 18 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 19 | import com.facebook.react.bridge.ReactMethod; 20 | import com.facebook.react.bridge.ReactModuleWithSpec; 21 | import com.facebook.react.bridge.ReadableMap; 22 | import com.facebook.react.turbomodule.core.interfaces.TurboModule; 23 | 24 | public abstract class NativeModuleMaterialTimePickerSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule { 25 | public NativeModuleMaterialTimePickerSpec(ReactApplicationContext reactContext) { 26 | super(reactContext); 27 | } 28 | 29 | @ReactMethod 30 | @DoNotStrip 31 | public abstract void dismiss(Promise promise); 32 | 33 | @ReactMethod 34 | @DoNotStrip 35 | public abstract void open(ReadableMap params, Promise promise); 36 | } 37 | -------------------------------------------------------------------------------- /ios/fabric/cpp/react/renderer/components/RNDateTimePicker/ComponentDescriptors.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 4 | * and copied to the cpp directory to override the adopt function and set the size of the shadow node based 5 | * on the state. 6 | * @generated by codegen project: GenerateComponentDescriptorH.js 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "ShadowNodes.h" 12 | #include 13 | 14 | namespace facebook { 15 | namespace react { 16 | 17 | class RNDateTimePickerComponentDescriptor final : public ConcreteComponentDescriptor { 18 | public: 19 | using ConcreteComponentDescriptor::ConcreteComponentDescriptor; 20 | 21 | void adopt(ShadowNode& shadowNode) const override { 22 | auto& pickerShadowNode = static_cast(shadowNode); 23 | auto& layoutableShadowNode = static_cast(pickerShadowNode); 24 | 25 | auto state = std::static_pointer_cast(shadowNode.getState()); 26 | auto stateData = state->getData(); 27 | 28 | if(stateData.frameSize.width != 0 && stateData.frameSize.height != 0) { 29 | layoutableShadowNode.setSize(Size{stateData.frameSize.width, stateData.frameSize.height}); 30 | } 31 | 32 | ConcreteComponentDescriptor::adopt(shadowNode); 33 | } 34 | }; 35 | 36 | } // namespace react 37 | } // namespace facebook 38 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Summary 4 | 5 | 13 | 14 | ## Test Plan 15 | 16 | 17 | 18 | ### What's required for testing (prerequisites)? 19 | 20 | ### What are the steps to reproduce (after prerequisites)? 21 | 22 | ## Compatibility 23 | 24 | 25 | 26 | | OS | Implemented | 27 | | ------- | :---------: | 28 | | iOS | ✅ ❌ | 29 | | Android | ✅ ❌ | 30 | 31 | ## Checklist 32 | 33 | 34 | 35 | - [ ] I have tested this on a device and a simulator 36 | - [ ] I added the documentation in `README.md` 37 | - [ ] I updated the typed files (TS and Flow) 38 | - [ ] I added a sample use of the API in the example project (`example/App.js`) 39 | - [ ] I have added automated tests, either in JS or e2e tests, as applicable 40 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @flow strict-local 4 | */ 5 | export const MIN_MS = 60000; 6 | 7 | export const ANDROID_DISPLAY = Object.freeze({ 8 | default: 'default', 9 | spinner: 'spinner', 10 | 11 | // NOTE: the following are exposed, but the native module instead uses "default" 12 | clock: 'clock', 13 | calendar: 'calendar', 14 | }); 15 | 16 | export const EVENT_TYPE_SET = 'set'; 17 | export const EVENT_TYPE_DISMISSED = 'dismissed'; 18 | export const ANDROID_EVT_TYPE = Object.freeze({ 19 | set: EVENT_TYPE_SET, 20 | dismissed: EVENT_TYPE_DISMISSED, 21 | neutralButtonPressed: 'neutralButtonPressed', 22 | }); 23 | 24 | export const IOS_DISPLAY = Object.freeze({ 25 | default: 'default', 26 | spinner: 'spinner', 27 | compact: 'compact', 28 | inline: 'inline', 29 | }); 30 | 31 | const COMMON_MODES = Object.freeze({ 32 | date: 'date', 33 | time: 'time', 34 | }); 35 | 36 | export const ANDROID_MODE = COMMON_MODES; 37 | 38 | export const WINDOWS_MODE = COMMON_MODES; 39 | 40 | export const IOS_MODE = Object.freeze({ 41 | ...COMMON_MODES, 42 | datetime: 'datetime', 43 | countdown: 'countdown', 44 | }); 45 | 46 | export const DAY_OF_WEEK = Object.freeze({ 47 | Sunday: 0, 48 | Monday: 1, 49 | Tuesday: 2, 50 | Wednesday: 3, 51 | Thursday: 4, 52 | Friday: 5, 53 | Saturday: 6, 54 | }); 55 | 56 | export const DATE_SET_ACTION = 'dateSetAction'; 57 | export const TIME_SET_ACTION = 'timeSetAction'; 58 | export const DISMISS_ACTION = 'dismissedAction'; 59 | 60 | export const NEUTRAL_BUTTON_ACTION = 'neutralButtonAction'; 61 | -------------------------------------------------------------------------------- /src/specs/DateTimePickerNativeComponent.js: -------------------------------------------------------------------------------- 1 | // @flow strict-local 2 | import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes'; 3 | import type {ColorValue} from 'react-native/Libraries/StyleSheet/StyleSheet'; 4 | import type {HostComponent} from 'react-native'; 5 | 6 | import type { 7 | BubblingEventHandler, 8 | Double, 9 | Int32, 10 | WithDefault, 11 | } from 'react-native/Libraries/Types/CodegenTypes'; 12 | import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; 13 | 14 | type DateTimePickerEvent = $ReadOnly<{| 15 | timestamp: Double, 16 | utcOffset: Int32, 17 | |}>; 18 | 19 | type NativeProps = $ReadOnly<{| 20 | ...ViewProps, 21 | accentColor?: ?ColorValue, 22 | date?: ?Double, 23 | displayIOS?: WithDefault< 24 | 'default' | 'spinner' | 'compact' | 'inline', 25 | 'default', 26 | >, 27 | locale?: ?string, 28 | maximumDate?: ?Double, 29 | minimumDate?: ?Double, 30 | minuteInterval?: ?Int32, 31 | mode?: WithDefault<'date' | 'time' | 'datetime' | 'countdown', 'date'>, 32 | onChange?: ?BubblingEventHandler, 33 | onPickerDismiss?: ?BubblingEventHandler, 34 | textColor?: ?ColorValue, 35 | themeVariant?: WithDefault<'dark' | 'light' | 'unspecified', 'unspecified'>, 36 | timeZoneName?: ?string, 37 | timeZoneOffsetInMinutes?: ?Double, 38 | enabled?: WithDefault, 39 | |}>; 40 | 41 | export default (codegenNativeComponent('RNDateTimePicker', { 42 | excludedPlatforms: ['android'], 43 | interfaceOnly: true, 44 | }): HostComponent); 45 | -------------------------------------------------------------------------------- /react-native.config.js: -------------------------------------------------------------------------------- 1 | const project = (() => { 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | try { 5 | const {configureProjects} = require('react-native-test-app'); 6 | 7 | return configureProjects({ 8 | android: { 9 | sourceDir: path.join('example', 'android'), 10 | manifestPath: path.join(__dirname, 'example', 'android'), 11 | }, 12 | ios: { 13 | sourceDir: 'example/ios', 14 | }, 15 | windows: fs.existsSync( 16 | 'example/windows/date-time-picker-example.sln', 17 | ) && { 18 | sourceDir: path.join('example', 'windows'), 19 | solutionFile: path.join( 20 | 'example', 21 | 'windows', 22 | 'date-time-picker-example.sln', 23 | ), 24 | project: path.join(__dirname, 'example', 'windows'), 25 | }, 26 | }); 27 | } catch (e) { 28 | return undefined; 29 | } 30 | })(); 31 | 32 | module.exports = { 33 | dependency: { 34 | platforms: { 35 | windows: { 36 | sourceDir: 'windows', 37 | solutionFile: 'DateTimePickerWindows.sln', 38 | }, 39 | }, 40 | }, 41 | dependencies: { 42 | ...(project 43 | ? { 44 | // Help rn-cli find and autolink this library 45 | '@react-native-community/datetimepicker': { 46 | root: __dirname, 47 | }, 48 | 'expo': { 49 | // otherwise RN cli will try to autolink expo 50 | platforms: { 51 | ios: null, 52 | android: null, 53 | }, 54 | }, 55 | } 56 | : undefined), 57 | }, 58 | ...(project ? {project} : undefined), 59 | }; 60 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore polyfills 9 | node_modules/react-native/Libraries/polyfills/.* 10 | .*/**/malformed_package_json/package.json 11 | 12 | 13 | ; Flow doesn't support platforms 14 | .*/Libraries/Utilities/LoadingView.js 15 | 16 | [untyped] 17 | .*/node_modules/@react-native-community/cli/.*/.* 18 | .*/test/.* 19 | 20 | [include] 21 | 22 | [libs] 23 | node_modules/react-native/interface.js 24 | node_modules/react-native/flow/ 25 | 26 | [options] 27 | emoji=true 28 | 29 | exact_by_default=true 30 | 31 | format.bracket_spacing=false 32 | 33 | module.file_ext=.js 34 | module.file_ext=.json 35 | module.file_ext=.ios.js 36 | 37 | munge_underscores=true 38 | 39 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 40 | module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' 41 | 42 | suppress_type=$FlowIssue 43 | suppress_type=$FlowFixMe 44 | suppress_type=$FlowFixMeProps 45 | suppress_type=$FlowFixMeState 46 | 47 | [lints] 48 | sketchy-null-number=warn 49 | sketchy-null-mixed=warn 50 | sketchy-number=warn 51 | untyped-type-import=warn 52 | nonstrict-import=warn 53 | deprecated-type=warn 54 | unsafe-getters-setters=warn 55 | unnecessary-invariant=warn 56 | 57 | [strict] 58 | deprecated-type 59 | nonstrict-import 60 | sketchy-null 61 | unclear-type 62 | unsafe-getters-setters 63 | untyped-import 64 | untyped-type-import 65 | 66 | [version] 67 | ^0.217.0 68 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactcommunity/rndatetimepicker/MaterialDatePickerModule.kt: -------------------------------------------------------------------------------- 1 | package com.reactcommunity.rndatetimepicker 2 | 3 | import androidx.fragment.app.FragmentActivity 4 | import com.facebook.react.bridge.Promise 5 | import com.facebook.react.bridge.ReactApplicationContext 6 | import com.facebook.react.bridge.ReadableMap 7 | import com.facebook.react.bridge.UiThreadUtil 8 | import com.reactcommunity.rndatetimepicker.Common.createDatePickerArguments 9 | import com.reactcommunity.rndatetimepicker.Common.dismissDialog 10 | 11 | class MaterialDatePickerModule(reactContext: ReactApplicationContext): NativeModuleMaterialDatePickerSpec(reactContext) { 12 | companion object { 13 | const val NAME = "RNCMaterialDatePicker" 14 | } 15 | 16 | override fun getName(): String { 17 | return NAME 18 | } 19 | 20 | override fun dismiss(promise: Promise?) { 21 | val activity = reactApplicationContext.currentActivity as FragmentActivity? 22 | dismissDialog(activity, NAME, promise) 23 | } 24 | 25 | override fun open(params: ReadableMap, promise: Promise) { 26 | val activity = reactApplicationContext.currentActivity as FragmentActivity? 27 | if (activity == null) { 28 | promise.reject( 29 | RNConstants.ERROR_NO_ACTIVITY, 30 | "Tried to open a MaterialDatePicker dialog while not attached to an Activity" 31 | ) 32 | return 33 | } 34 | 35 | val fragmentManager = activity.supportFragmentManager 36 | 37 | UiThreadUtil.runOnUiThread { 38 | val arguments = createDatePickerArguments(params) 39 | val datePicker = 40 | RNMaterialDatePicker(arguments, promise, fragmentManager, reactApplicationContext) 41 | datePicker.open() 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactcommunity/rndatetimepicker/RNConstants.java: -------------------------------------------------------------------------------- 1 | package com.reactcommunity.rndatetimepicker; 2 | 3 | import android.app.TimePickerDialog; 4 | 5 | public final class RNConstants { 6 | public static final String ERROR_NO_ACTIVITY = "E_NO_ACTIVITY"; 7 | public static final String ARG_VALUE = "value"; 8 | public static final String ARG_MINDATE = "minimumDate"; 9 | public static final String ARG_MAXDATE = "maximumDate"; 10 | public static final String ARG_INTERVAL = "minuteInterval"; 11 | public static final String ARG_IS24HOUR = "is24Hour"; 12 | public static final String ARG_DISPLAY = "display"; 13 | public static final String ARG_DIALOG_BUTTONS = "dialogButtons"; 14 | public static final String ARG_TZOFFSET_MINS = "timeZoneOffsetInMinutes"; 15 | public static final String ARG_TZ_NAME = "timeZoneName"; 16 | public static final String ARG_TESTID = "testID"; 17 | public static final String ARG_TITLE = "title"; 18 | public static final String ARG_INITIAL_INPUT_MODE = "initialInputMode"; 19 | public static final String ARG_FULLSCREEN = "fullscreen"; 20 | public static final String ACTION_DATE_SET = "dateSetAction"; 21 | public static final String ACTION_TIME_SET = "timeSetAction"; 22 | public static final String ACTION_DISMISSED = "dismissedAction"; 23 | public static final String ACTION_NEUTRAL_BUTTON = "neutralButtonAction"; 24 | public static final String FIRST_DAY_OF_WEEK = "firstDayOfWeek"; 25 | 26 | /** 27 | * Minimum date supported by {@link TimePickerDialog}, 01 Jan 1900 28 | */ 29 | public static final long DEFAULT_MIN_DATE = -2208988800001l; 30 | 31 | /** 32 | * Minimum and default time picker minute interval 33 | */ 34 | public static final int DEFAULT_TIME_PICKER_INTERVAL = 1; 35 | } 36 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/DateTimePickerView.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include "DateTimePickerView.g.h" 7 | #include "winrt/Microsoft.ReactNative.h" 8 | #include "NativeModules.h" 9 | 10 | namespace winrt::DateTimePicker::implementation { 11 | 12 | namespace xaml = winrt::Windows::UI::Xaml; 13 | 14 | class DateTimePickerView : public DateTimePickerViewT { 15 | public: 16 | DateTimePickerView(Microsoft::ReactNative::IReactContext const& reactContext); 17 | void UpdateProperties(Microsoft::ReactNative::IJSValueReader const& reader); 18 | 19 | private: 20 | Microsoft::ReactNative::IReactContext m_reactContext{ nullptr }; 21 | bool m_updating{ false }; 22 | xaml::Controls::CalendarDatePicker::DateChanged_revoker m_dataPickerDateChangedRevoker{}; 23 | 24 | void RegisterEvents(); 25 | void OnDateChanged(winrt::Windows::Foundation::IInspectable const& sender, xaml::Controls::CalendarDatePickerDateChangedEventArgs const& args); 26 | winrt::Windows::Foundation::DateTime DateTimeFrom(int64_t timeInMilliSeconds, int64_t timeZoneOffsetInSeconds); 27 | int64_t DateTimeToMiliseconds(winrt::Windows::Foundation::DateTime dateTime, int64_t timeZoneOffsetInSeconds); 28 | 29 | int64_t m_selectedTime, m_maxTime, m_minTime; // These values are expected to be in milliseconds. 30 | int64_t m_timeZoneOffsetInSeconds = 0; // Timezone offset is expected to be in seconds. 31 | }; 32 | } 33 | 34 | namespace winrt::DateTimePicker::factory_implementation { 35 | struct DateTimePickerView : DateTimePickerViewT {}; 36 | } -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/DateTimePickerWindows.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | accd3aa8-1ba0-4223-9bbe-0c431709210b 6 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms 7 | 8 | 9 | {926ab91d-31b4-48c3-b9a4-e681349f27f0} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Resources 42 | 43 | 44 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactcommunity/rndatetimepicker/MaterialTimePickerModule.kt: -------------------------------------------------------------------------------- 1 | package com.reactcommunity.rndatetimepicker 2 | 3 | import androidx.fragment.app.FragmentActivity 4 | import com.facebook.react.bridge.Promise 5 | import com.facebook.react.bridge.ReactApplicationContext 6 | import com.facebook.react.bridge.ReadableMap 7 | import com.facebook.react.bridge.UiThreadUtil 8 | import com.reactcommunity.rndatetimepicker.Common.createTimePickerArguments 9 | import com.reactcommunity.rndatetimepicker.Common.dismissDialog 10 | 11 | class MaterialTimePickerModule(reactContext: ReactApplicationContext) : 12 | NativeModuleMaterialTimePickerSpec(reactContext) { 13 | companion object { 14 | const val NAME = "RNCMaterialTimePicker" 15 | } 16 | 17 | override fun getName(): String { 18 | return NAME 19 | } 20 | 21 | override fun dismiss(promise: Promise?) { 22 | val activity = reactApplicationContext.currentActivity as FragmentActivity? 23 | dismissDialog(activity, NAME, promise) 24 | } 25 | 26 | override fun open(params: ReadableMap, promise: Promise) { 27 | val activity = reactApplicationContext.currentActivity as FragmentActivity? 28 | if (activity == null) { 29 | promise.reject( 30 | RNConstants.ERROR_NO_ACTIVITY, 31 | "Tried to open a MaterialTimePicker dialog while not attached to an Activity" 32 | ) 33 | } 34 | 35 | val fragmentManager = activity!!.supportFragmentManager 36 | 37 | UiThreadUtil.runOnUiThread { 38 | val arguments = 39 | createTimePickerArguments(params) 40 | val materialPicker = RNMaterialTimePicker( 41 | arguments, 42 | promise, 43 | fragmentManager, 44 | reactApplicationContext 45 | ) 46 | materialPicker.open() 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.github/label-actions.yml: -------------------------------------------------------------------------------- 1 | # Configuration for Label Actions - https://github.com/dessant/label-actions 2 | 3 | # Actions taken when the `repro-required` label is added 4 | repro-required: 5 | # Post a comment 6 | comment: > 7 | :wave: @{issue-author}, sorry you're having an issue. This issue is being closed because it does not provide all information required by the [issue template](https://raw.githubusercontent.com/react-native-datetimepicker/.github/master/.github/ISSUE_TEMPLATE/bug_report.md). 8 | As the issue template explains, we require that you provide a runnable example that reproduces your issue and your environment information. 9 | This means you need to provide a code snippet that we can copy-paste into an empty project and see the error ourselves, or provide a git repository with the issue. 10 | 11 | The reason is that maintainers do not have time to try reproduce bugs themselves. Please try to minimize the superfluous code and focus only on reproducing the bug. 12 | 13 | Please create a new issue with this and we'll be happy to review it! 14 | # Lock the thread 15 | lock: true 16 | close: true 17 | # Actions taken when the `repro-required` label is removed 18 | -repro-required: 19 | # Unlock the thread 20 | unlock: true 21 | reopen: true 22 | 23 | question: 24 | # Post a comment 25 | comment: > 26 | :wave: @{issue-author}, thanks for opening the issue. The issue tracker is intended for tracking bug reports and feature requests only. 27 | 28 | Seems you have a usage question. Please ask the question on [StackOverflow](https://stackoverflow.com/questions/tagged/react-native). You can also chat with other community members on [Reactiflux Discord server](https://www.reactiflux.com/). 29 | # Lock the thread 30 | lock: true 31 | close: true 32 | # Actions taken when the `repro-required` label is removed 33 | -question: 34 | # Unlock the thread 35 | unlock: true 36 | reopen: true 37 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/TimePickerViewManager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include "winrt/Microsoft.ReactNative.h" 7 | #include "NativeModules.h" 8 | 9 | namespace winrt::DateTimePicker::implementation { 10 | 11 | class TimePickerViewManager : public winrt::implements< 12 | TimePickerViewManager, 13 | winrt::Microsoft::ReactNative::IViewManager, 14 | winrt::Microsoft::ReactNative::IViewManagerWithReactContext, 15 | winrt::Microsoft::ReactNative::IViewManagerWithNativeProperties, 16 | winrt::Microsoft::ReactNative::IViewManagerWithExportedEventTypeConstants> { 17 | public: 18 | TimePickerViewManager(); 19 | 20 | // IViewManager 21 | winrt::hstring Name() noexcept; 22 | winrt::Windows::UI::Xaml::FrameworkElement CreateView() noexcept; 23 | 24 | // IViewManagerWithReactContext 25 | winrt::Microsoft::ReactNative::IReactContext ReactContext() noexcept; 26 | void ReactContext(winrt::Microsoft::ReactNative::IReactContext reactContext) noexcept; 27 | 28 | // IViewManagerWithNativeProperties 29 | winrt::Windows::Foundation::Collections:: 30 | IMapView 31 | NativeProps() noexcept; 32 | 33 | void UpdateProperties( 34 | winrt::Windows::UI::Xaml::FrameworkElement const& view, 35 | winrt::Microsoft::ReactNative::IJSValueReader const& propertyMapReader) noexcept; 36 | 37 | // IViewManagerWithExportedEventTypeConstants 38 | winrt::Microsoft::ReactNative::ConstantProviderDelegate ExportedCustomBubblingEventTypeConstants() noexcept; 39 | winrt::Microsoft::ReactNative::ConstantProviderDelegate ExportedCustomDirectEventTypeConstants() noexcept; 40 | 41 | private: 42 | winrt::Microsoft::ReactNative::IReactContext m_reactContext{ nullptr }; 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/DateTimePickerViewManager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include "winrt/Microsoft.ReactNative.h" 7 | #include "NativeModules.h" 8 | 9 | namespace winrt::DateTimePicker::implementation { 10 | 11 | class DateTimePickerViewManager : public winrt::implements< 12 | DateTimePickerViewManager, 13 | winrt::Microsoft::ReactNative::IViewManager, 14 | winrt::Microsoft::ReactNative::IViewManagerWithReactContext, 15 | winrt::Microsoft::ReactNative::IViewManagerWithNativeProperties, 16 | winrt::Microsoft::ReactNative::IViewManagerWithExportedEventTypeConstants> { 17 | public: 18 | DateTimePickerViewManager(); 19 | 20 | // IViewManager 21 | winrt::hstring Name() noexcept; 22 | winrt::Windows::UI::Xaml::FrameworkElement CreateView() noexcept; 23 | 24 | // IViewManagerWithReactContext 25 | winrt::Microsoft::ReactNative::IReactContext ReactContext() noexcept; 26 | void ReactContext(winrt::Microsoft::ReactNative::IReactContext reactContext) noexcept; 27 | 28 | // IViewManagerWithNativeProperties 29 | winrt::Windows::Foundation::Collections:: 30 | IMapView 31 | NativeProps() noexcept; 32 | 33 | void UpdateProperties( 34 | winrt::Windows::UI::Xaml::FrameworkElement const& view, 35 | winrt::Microsoft::ReactNative::IJSValueReader const& propertyMapReader) noexcept; 36 | 37 | // IViewManagerWithExportedEventTypeConstants 38 | winrt::Microsoft::ReactNative::ConstantProviderDelegate ExportedCustomBubblingEventTypeConstants() noexcept; 39 | winrt::Microsoft::ReactNative::ConstantProviderDelegate ExportedCustomDirectEventTypeConstants() noexcept; 40 | 41 | private: 42 | winrt::Microsoft::ReactNative::IReactContext m_reactContext{ nullptr }; 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @flow strict-local 4 | */ 5 | import type {DatePickerOptions, TimePickerOptions} from './types'; 6 | import invariant from 'invariant'; 7 | 8 | /** 9 | * Convert a Date to a timestamp. 10 | */ 11 | export function toMilliseconds( 12 | options: DatePickerOptions | TimePickerOptions, 13 | ...keys: Array 14 | ) { 15 | keys.forEach(function each(key) { 16 | const value = options[key]; 17 | 18 | // Is it a valid Date object? 19 | // $FlowFixMe: Cannot get `Object.prototype.toString` because property `toString` [1] cannot be unbound from the context [2] where it was defined. 20 | if (Object.prototype.toString.call(value) === '[object Date]') { 21 | // $FlowFixMe[prop-missing] 22 | options[key] = value.getTime(); 23 | } 24 | }); 25 | } 26 | 27 | export function dateToMilliseconds(date: ?Date): ?number { 28 | if (!date) { 29 | return; 30 | } 31 | return date.getTime(); 32 | } 33 | 34 | export function sharedPropsValidation({ 35 | value, 36 | timeZoneName, 37 | timeZoneOffsetInMinutes, 38 | minimumDate, 39 | maximumDate, 40 | }: { 41 | value: Date, 42 | timeZoneName?: ?string, 43 | timeZoneOffsetInMinutes?: ?number, 44 | minimumDate?: ?Date, 45 | maximumDate?: ?Date, 46 | }) { 47 | invariant(value, 'A date or time must be specified as `value` prop'); 48 | invariant( 49 | value instanceof Date, 50 | '`value` prop must be an instance of Date object', 51 | ); 52 | invariant( 53 | timeZoneName == null || timeZoneOffsetInMinutes == null, 54 | '`timeZoneName` and `timeZoneOffsetInMinutes` cannot be specified at the same time', 55 | ); 56 | 57 | if (minimumDate && maximumDate) { 58 | invariant( 59 | minimumDate <= maximumDate, 60 | `DateTimePicker: minimumDate (${minimumDate.toISOString()}) is after maximumDate (${maximumDate.toISOString()}). Ensure minimumDate < maximumDate.`, 61 | ); 62 | } 63 | 64 | if (timeZoneOffsetInMinutes !== undefined) { 65 | console.warn( 66 | '`timeZoneOffsetInMinutes` is deprecated and will be removed in a future release. Use `timeZoneName` instead.', 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/materialtimepicker.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @format 8 | * @flow strict-local 9 | */ 10 | import {TIME_SET_ACTION, DISMISS_ACTION, ANDROID_DISPLAY} from './constants'; 11 | import {toMilliseconds} from './utils'; 12 | import RNMaterialTimePickerAndroid from './specs/NativeModuleMaterialTimePicker'; 13 | import type {TimePickerOptions, DateTimePickerResult} from './types'; 14 | 15 | export default class MaterialTimePickerAndroid { 16 | /** 17 | * Opens the standard Android time picker dialog. 18 | * 19 | * The available keys for the `options` object are: 20 | * - `value` (`Date` object) - date to show by default 21 | * * `is24Hour` (boolean) - If `true`, the picker uses the 24-hour format. If `false`, 22 | * the picker shows an AM/PM chooser. If undefined, the default for the current locale 23 | * is used. 24 | * * `initialInputMode` (enum('default' | 'keyboard')) - sets the input mode for the time picker. 25 | * The user can still switch to the other input mode. The default is a clock. 26 | * * `title` (string) - set the title of the dialog 27 | * 28 | * Returns a Promise which will be invoked an object containing `action`, `hour` (0-23), 29 | * `minute` (0-59) if the user picked a time. If the user dismissed the dialog, the Promise will 30 | * still be resolved with action being `TimePickerAndroid.dismissedAction` and all the other keys 31 | * being undefined. **Always** check whether the `action` before reading the values. 32 | */ 33 | static async open(options: TimePickerOptions): Promise { 34 | toMilliseconds(options, 'value'); 35 | options.display = options.display || ANDROID_DISPLAY.default; 36 | return RNMaterialTimePickerAndroid.open(options); 37 | } 38 | 39 | static async dismiss(): Promise { 40 | return RNMaterialTimePickerAndroid.dismiss(); 41 | } 42 | 43 | /** 44 | * A time has been selected. 45 | */ 46 | static +timeSetAction: 'timeSetAction' = TIME_SET_ACTION; 47 | /** 48 | * The dialog has been dismissed. 49 | */ 50 | static +dismissedAction: 'dismissedAction' = DISMISS_ACTION; 51 | } 52 | -------------------------------------------------------------------------------- /src/timepicker.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @format 8 | * @flow strict-local 9 | */ 10 | import {TIME_SET_ACTION, DISMISS_ACTION, ANDROID_DISPLAY} from './constants'; 11 | import {toMilliseconds} from './utils'; 12 | import RNTimePickerAndroid from './specs/NativeModuleTimePicker'; 13 | import type {TimePickerOptions, DateTimePickerResult} from './types'; 14 | 15 | export default class TimePickerAndroid { 16 | /** 17 | * Opens the standard Android time picker dialog. 18 | * 19 | * The available keys for the `options` object are: 20 | * - `value` (`Date` object) - date to show by default 21 | * * `is24Hour` (boolean) - If `true`, the picker uses the 24-hour format. If `false`, 22 | * the picker shows an AM/PM chooser. If undefined, the default for the current locale 23 | * is used. 24 | * * `minuteInterval` (enum(1 | 5 | 10 | 15 | 20 | 30)`) - set the time picker minutes' interval 25 | * * `display` (`enum('clock', 'spinner', 'default')`) - set the time picker mode 26 | * - 'clock': Show a time picker in clock mode. 27 | * - 'spinner': Show a time picker in spinner mode. 28 | * - 'default': Show a default time picker based on Android versions. 29 | * 30 | * Returns a Promise which will be invoked an object containing `action`, `hour` (0-23), 31 | * `minute` (0-59) if the user picked a time. If the user dismissed the dialog, the Promise will 32 | * still be resolved with action being `TimePickerAndroid.dismissedAction` and all the other keys 33 | * being undefined. **Always** check whether the `action` before reading the values. 34 | */ 35 | static async open(options: TimePickerOptions): Promise { 36 | toMilliseconds(options, 'value'); 37 | options.display = options.display || ANDROID_DISPLAY.default; 38 | return RNTimePickerAndroid.open(options); 39 | } 40 | 41 | static async dismiss(): Promise { 42 | return RNTimePickerAndroid.dismiss(); 43 | } 44 | 45 | /** 46 | * A time has been selected. 47 | */ 48 | static +timeSetAction: 'timeSetAction' = TIME_SET_ACTION; 49 | /** 50 | * The dialog has been dismissed. 51 | */ 52 | static +dismissedAction: 'dismissedAction' = DISMISS_ACTION; 53 | } 54 | -------------------------------------------------------------------------------- /ios/RNDateTimePicker.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import "RNDateTimePicker.h" 9 | 10 | #import 11 | #import 12 | 13 | @interface RNDateTimePicker () 14 | 15 | @property (nonatomic, copy) RCTBubblingEventBlock onChange; 16 | @property (nonatomic, copy) RCTBubblingEventBlock onPickerDismiss; 17 | @property (nonatomic, assign) NSInteger reactMinuteInterval; 18 | 19 | @end 20 | 21 | @implementation RNDateTimePicker 22 | 23 | - (instancetype)initWithFrame:(CGRect)frame 24 | { 25 | if ((self = [super initWithFrame:frame])) { 26 | #ifndef RCT_NEW_ARCH_ENABLED 27 | // somehow, with Fabric, the callbacks are executed here as well as in RNDateTimePickerComponentView 28 | // so do not register it with Fabric, to avoid potential problems 29 | [self addTarget:self action:@selector(didChange) 30 | forControlEvents:UIControlEventValueChanged]; 31 | [self addTarget:self action:@selector(onDismiss:) forControlEvents:UIControlEventEditingDidEnd]; 32 | #endif 33 | 34 | _reactMinuteInterval = 1; 35 | } 36 | return self; 37 | } 38 | 39 | RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) 40 | 41 | - (void)didChange 42 | { 43 | if (_onChange) { 44 | _onChange(@{ @"timestamp": @(self.date.timeIntervalSince1970 * 1000.0), @"utcOffset": @([self.timeZone secondsFromGMTForDate:self.date] / 60 )}); 45 | } 46 | } 47 | 48 | - (void)onDismiss:(RNDateTimePicker *)sender 49 | { 50 | if (_onPickerDismiss) { 51 | _onPickerDismiss(@{}); 52 | } 53 | } 54 | 55 | - (void)setDatePickerMode:(UIDatePickerMode)datePickerMode 56 | { 57 | [super setDatePickerMode:datePickerMode]; 58 | // We need to set minuteInterval after setting datePickerMode, otherwise minuteInterval is invalid in time mode. 59 | self.minuteInterval = _reactMinuteInterval; 60 | } 61 | 62 | - (void)setMinuteInterval:(NSInteger)minuteInterval 63 | { 64 | [super setMinuteInterval:minuteInterval]; 65 | _reactMinuteInterval = minuteInterval; 66 | } 67 | 68 | - (void)setDate:(NSDate *)date { 69 | // Need to avoid the case where values coming back through the bridge trigger a new valueChanged event 70 | if (![self.date isEqualToDate:date]) { 71 | [super setDate:date animated:NO]; 72 | } 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | def getExtOrIntegerDefault(name) { 9 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['ReactNativeDateTimePicker_' + name]).toInteger() 10 | } 11 | 12 | def isNewArchitectureEnabled() { 13 | // To opt-in for the New Architecture, you can either: 14 | // - Set `newArchEnabled` to true inside the `gradle.properties` file 15 | // - Invoke gradle with `-newArchEnabled=true` 16 | // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true` 17 | return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" 18 | } 19 | 20 | apply plugin: 'com.android.library' 21 | apply plugin: 'org.jetbrains.kotlin.android' 22 | if (isNewArchitectureEnabled()) { 23 | apply plugin: "com.facebook.react" 24 | } 25 | 26 | 27 | android { 28 | def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION 29 | if (agpVersion.tokenize('.')[0].toInteger() >= 7) { 30 | namespace "com.reactcommunity.rndatetimepicker" 31 | } 32 | 33 | compileSdkVersion getExtOrIntegerDefault('compileSdkVersion') 34 | 35 | // Used to override the NDK path/version on internal CI or by allowing 36 | // users to customize the NDK path/version from their root project (e.g. for M1 support) 37 | if (rootProject.hasProperty("ndkPath")) { 38 | ndkPath rootProject.ext.ndkPath 39 | } 40 | if (rootProject.hasProperty("ndkVersion")) { 41 | ndkVersion rootProject.ext.ndkVersion 42 | } 43 | 44 | defaultConfig { 45 | minSdkVersion getExtOrIntegerDefault('minSdkVersion') 46 | targetSdkVersion getExtOrIntegerDefault('targetSdkVersion') 47 | buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() 48 | } 49 | 50 | sourceSets.main { 51 | java { 52 | if (!isNewArchitectureEnabled()) { 53 | srcDirs += 'src/paper/java' 54 | } 55 | } 56 | } 57 | kotlinOptions { 58 | jvmTarget = '17' 59 | } 60 | } 61 | 62 | repositories { 63 | google() 64 | mavenLocal() 65 | mavenCentral() 66 | } 67 | 68 | dependencies { 69 | //noinspection GradleDynamicVersion 70 | implementation 'com.facebook.react:react-native:+' 71 | implementation 'com.google.android.material:material:1.12.0' 72 | implementation 'androidx.core:core-ktx:1.13.1' 73 | } 74 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the Gradle Daemon. The setting is 11 | # particularly useful for configuring JVM memory settings for build performance. 12 | # This does not affect the JVM settings for the Gradle client VM. 13 | # The default is `-Xmx512m -XX:MaxMetaspaceSize=256m`. 14 | org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 15 | 16 | # When configured, Gradle will fork up to org.gradle.workers.max JVMs to execute 17 | # projects in parallel. To learn more about parallel task execution, see the 18 | # section on Gradle build performance: 19 | # https://docs.gradle.org/current/userguide/performance.html#parallel_execution. 20 | # Default is `false`. 21 | #org.gradle.parallel=true 22 | 23 | # AndroidX package structure to make it clearer which packages are bundled with the 24 | # Android operating system, and which are packaged with your app's APK 25 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 26 | android.useAndroidX=true 27 | # Automatically convert third-party libraries to use AndroidX 28 | android.enableJetifier=false 29 | # Jetifier randomly fails on these libraries 30 | android.jetifier.ignorelist=hermes-android 31 | 32 | # Use this property to specify which architecture you want to build. 33 | # You can also override it from the CLI using 34 | # ./gradlew -PreactNativeArchitectures=x86_64 35 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 36 | 37 | # Use this property to enable support to the new architecture. 38 | # This will allow you to use TurboModules and the Fabric render in 39 | # your application. You should enable this flag either if you want 40 | # to write custom TurboModules/Fabric components OR use libraries that 41 | # are providing them. 42 | # Note that this is incompatible with web debugging. 43 | newArchEnabled=true 44 | bridgelessEnabled=true 45 | 46 | # Uncomment the line below to build React Native from source. 47 | #react.buildFromSource=true 48 | 49 | # Version of Android NDK to build against. 50 | #ANDROID_NDK_VERSION=26.1.10909125 51 | 52 | # Version of Kotlin to build against. 53 | #KOTLIN_VERSION=1.8.22 54 | -------------------------------------------------------------------------------- /src/datepicker.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @format 8 | * @flow strict-local 9 | */ 10 | import {DATE_SET_ACTION, DISMISS_ACTION, ANDROID_DISPLAY} from './constants'; 11 | import {toMilliseconds} from './utils'; 12 | import RNDatePickerAndroid from './specs/NativeModuleDatePicker'; 13 | import type {DatePickerOptions, DateTimePickerResult} from './types'; 14 | 15 | export default class DatePickerAndroid { 16 | /** 17 | * Opens the standard Android date picker dialog. 18 | * 19 | * The available keys for the `options` object are: 20 | * 21 | * - `value` (`Date` object) - date to show by default 22 | * - `minimumDate` (`Date` object) - minimum date that can be selected 23 | * - `maximumDate` (`Date` object) - maximum date that can be selected 24 | * - `testID` (`string`) - Sets view tag for use with automation frameworks 25 | * - `display` (`enum('calendar', 'spinner', 'default')`) - To set the date-picker display to calendar/spinner/default 26 | * - 'calendar': Show a date picker in calendar mode. 27 | * - 'spinner': Show a date picker in spinner mode. 28 | * - 'default': Show a default native date picker(spinner/calendar) based on android versions. 29 | * 30 | * Returns a Promise which will be invoked an object containing `action`, `year`, `month` (0-11), 31 | * `day` if the user picked a date. If the user dismissed the dialog, the Promise will 32 | * still be resolved with action being `DatePickerAndroid.dismissedAction` and all the other keys 33 | * being undefined. **Always** check whether the `action` before reading the values. 34 | * 35 | * Note the native date picker dialog has some UI glitches on Android 4 and lower 36 | * when using the `minimumDate` and `maximumDate` options. 37 | */ 38 | static async open(options: DatePickerOptions): Promise { 39 | toMilliseconds(options, 'value', 'minimumDate', 'maximumDate'); 40 | options.display = options.display || ANDROID_DISPLAY.default; 41 | 42 | return RNDatePickerAndroid.open(options); 43 | } 44 | 45 | static async dismiss(): Promise { 46 | return RNDatePickerAndroid.dismiss(); 47 | } 48 | 49 | /** 50 | * A date has been selected. 51 | */ 52 | static +dateSetAction: 'dateSetAction' = DATE_SET_ACTION; 53 | /** 54 | * The dialog has been dismissed. 55 | */ 56 | static +dismissedAction: 'dismissedAction' = DISMISS_ACTION; 57 | } 58 | -------------------------------------------------------------------------------- /src/materialdatepicker.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @format 8 | * @flow strict-local 9 | */ 10 | import {DATE_SET_ACTION, DISMISS_ACTION} from './constants'; 11 | import {toMilliseconds} from './utils'; 12 | import RNMaterialDatePickerAndroid from './specs/NativeModuleMaterialDatePicker'; 13 | import type {DatePickerOptions, DateTimePickerResult} from './types'; 14 | 15 | export default class MaterialDatePickerAndroid { 16 | /** 17 | * Opens the standard Android date picker dialog. 18 | * 19 | * The available keys for the `options` object are: 20 | * 21 | * - `value` (`Date` object) - date to show by default 22 | * - `minimumDate` (`Date` object) - minimum date that can be selected 23 | * - `maximumDate` (`Date` object) - maximum date that can be selected 24 | * * `initialInputMode` (enum('default' | 'keyboard')) - sets the input mode for the date picker. 25 | * The user can still switch to the other input mode. The default is a calendar. 26 | * All options are not available for the Material picker. 27 | * * `title` (string) - set the title of the dialog 28 | * * `fullscreen` (boolean) - set if the dialog is fullscreen 29 | * * `firstDayOfWeek` (int) - set what the calendar shows as the first day of the week 30 | * 31 | * Returns a Promise which will be invoked an object containing `action`, `year`, `month` (0-11), 32 | * `day` if the user picked a date. If the user dismissed the dialog, the Promise will 33 | * still be resolved with action being `DatePickerAndroid.dismissedAction` and all the other keys 34 | * being undefined. **Always** check whether the `action` before reading the values. 35 | * 36 | * Note the native date picker dialog has some UI glitches on Android 4 and lower 37 | * when using the `minimumDate` and `maximumDate` options. 38 | */ 39 | static async open(options: DatePickerOptions): Promise { 40 | toMilliseconds(options, 'value', 'minimumDate', 'maximumDate'); 41 | 42 | return RNMaterialDatePickerAndroid.open(options); 43 | } 44 | 45 | static async dismiss(): Promise { 46 | return RNMaterialDatePickerAndroid.dismiss(); 47 | } 48 | 49 | /** 50 | * A date has been selected. 51 | */ 52 | static +dateSetAction: 'dateSetAction' = DATE_SET_ACTION; 53 | /** 54 | * The dialog has been dismissed. 55 | */ 56 | static +dismissedAction: 'dismissedAction' = DISMISS_ACTION; 57 | } 58 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to the component 2 | 3 | Development can be done using the example app. Follow readme instructions on running the example app, open it in Xcode or Android Studio and you can start contributing! 4 | 5 | ### Clone, install 6 | 7 | ```sh 8 | git clone https://github.com/react-native-community/datetimepicker.git 9 | cd datetimepicker 10 | yarn 11 | ``` 12 | 13 | ### Tests 14 | 15 | #### Jest 16 | 17 | ```sh 18 | yarn 19 | yarn test 20 | ``` 21 | 22 | #### Detox 23 | 24 | Detox is a gray box end-to-end testing and automation library for mobile apps. 25 | 26 | - [Dependencies required](https://wix.github.io/Detox/docs/introduction/getting-started/#detox-prerequisites) 27 | 28 | For cleaning all the detox builds just run `npm run detox:clean`. 29 | 30 | ##### iOS 31 | 32 | - debug: 33 | 34 | ```sh 35 | # Debug requires to run Metro Bundler 36 | yarn start 37 | cd "example/ios" && npx pod-install && cd - 38 | yarn detox:ios:build:debug 39 | yarn detox:ios:test:debug 40 | ``` 41 | 42 | - release: 43 | 44 | ```sh 45 | yarn bundle:ios # we need to bundle js first 46 | cd "example/ios" && npx pod-install && cd - # run pod install to include bundle 47 | yarn detox:ios:build:release 48 | yarn detox:ios:test:release 49 | ``` 50 | 51 | ##### Android 52 | 53 | An existing Android emulator is required to match the name defined in `detox.configurations.android.emu.debug.name` and `detox.configurations.android.emu.release.name` inside the `package.json`. 54 | 55 | - debug: 56 | 57 | ```sh 58 | # Debug requires to run Metro Bundler 59 | yarn start 60 | yarn detox:android:build:debug 61 | yarn detox:android:test:debug 62 | ``` 63 | 64 | - release: 65 | 66 | ```sh 67 | yarn bundle:android # we need to bundle js first 68 | yarn detox:android:build:release 69 | yarn detox:android:test:release 70 | ``` 71 | 72 | ### Fabric 73 | 74 | Fabric is the new React Native rendering system ([read more about it here](https://reactnative.dev/architecture/fabric-renderer)). 75 | 76 | #### iOS 77 | 78 | ``` 79 | yarn start 80 | cd "example/ios" && RCT_NEW_ARCH_ENABLED=1 npx pod-install && cd - 81 | yarn start:ios 82 | ``` 83 | 84 | If you want to go back to the old renderer (Paper), 85 | remove `ios/build`, run `pod-install` without the `RCT_NEW_ARCH_ENABLED=1` and build again 86 | 87 | ``` 88 | rm -r "example/ios/build" 89 | cd "example/ios" && npx pod-install && cd - 90 | yarn start:ios 91 | ``` 92 | 93 | 94 | #### Android 95 | 96 | The date time picker does not have a native UI component for Android but a native module. 97 | ([read more about native modules here](https://reactnative.dev/docs/turbo-native-modules-introduction)). -------------------------------------------------------------------------------- /src/datetimepicker.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @flow strict-local 4 | */ 5 | import {ANDROID_DISPLAY, ANDROID_MODE} from './constants'; 6 | import {useEffect} from 'react'; 7 | 8 | import type {AndroidNativeProps} from './types'; 9 | import {validateAndroidProps} from './androidUtils'; 10 | import {DateTimePickerAndroid} from './DateTimePickerAndroid'; 11 | 12 | export default function RNDateTimePickerAndroid( 13 | props: AndroidNativeProps, 14 | ): null { 15 | validateAndroidProps(props); 16 | const { 17 | mode = ANDROID_MODE.date, 18 | display = ANDROID_DISPLAY.default, 19 | value, 20 | onChange, 21 | is24Hour, 22 | minimumDate, 23 | maximumDate, 24 | minuteInterval, 25 | onError, 26 | timeZoneOffsetInMinutes, 27 | timeZoneName, 28 | positiveButton, 29 | negativeButton, 30 | neutralButton, 31 | positiveButtonLabel, 32 | negativeButtonLabel, 33 | neutralButtonLabel, 34 | testID, 35 | firstDayOfWeek, 36 | title, 37 | initialInputMode, 38 | design, 39 | fullscreen, 40 | } = props; 41 | const valueTimestamp = value.getTime(); 42 | 43 | useEffect(() => { 44 | // This effect runs on unmount / with mode change, and will ensure the picker is closed. 45 | // This allows for controlling the opening state of the picker through declarative logic in jsx. 46 | return () => DateTimePickerAndroid.dismiss(mode, design); 47 | }, [mode, design]); 48 | 49 | useEffect( 50 | function showOrUpdatePicker() { 51 | const params = { 52 | mode, 53 | value: new Date(valueTimestamp), 54 | display, 55 | is24Hour, 56 | minimumDate, 57 | maximumDate, 58 | minuteInterval, 59 | timeZoneOffsetInMinutes, 60 | timeZoneName, 61 | onError, 62 | onChange, 63 | positiveButton, 64 | negativeButton, 65 | neutralButton, 66 | positiveButtonLabel, 67 | negativeButtonLabel, 68 | neutralButtonLabel, 69 | testID, 70 | firstDayOfWeek, 71 | title, 72 | initialInputMode, 73 | design, 74 | fullscreen, 75 | }; 76 | DateTimePickerAndroid.open(params); 77 | }, 78 | // the android dialog, when presented, will actually ignore updates to all props other than `value` 79 | // as an alternative, use the DateTimePickerAndroid whose reason for existence is described in 80 | // https://github.com/react-native-datetimepicker/datetimepicker/pull/327#issuecomment-723160992 81 | // eslint-disable-next-line react-hooks/exhaustive-deps 82 | [onChange, valueTimestamp, mode], 83 | ); 84 | 85 | return null; 86 | } 87 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/TimePickerViewManager.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | #include "pch.h" 5 | #include "TimePickerViewManager.h" 6 | #include "NativeModules.h" 7 | #include "TimePickerView.h" 8 | 9 | namespace winrt { 10 | using namespace Microsoft::ReactNative; 11 | using namespace Windows::Foundation::Collections; 12 | 13 | namespace xaml = winrt::Windows::UI::Xaml; 14 | } 15 | 16 | namespace winrt::DateTimePicker::implementation { 17 | 18 | TimePickerViewManager::TimePickerViewManager() {} 19 | 20 | // IViewManager 21 | winrt::hstring TimePickerViewManager::Name() noexcept { 22 | return L"RNTimePickerWindows"; 23 | } 24 | 25 | xaml::FrameworkElement TimePickerViewManager::CreateView() noexcept { 26 | return winrt::DateTimePicker::TimePickerView(m_reactContext); 27 | } 28 | 29 | // IViewManagerWithReactContext 30 | winrt::IReactContext TimePickerViewManager::ReactContext() noexcept { 31 | return m_reactContext; 32 | } 33 | 34 | void TimePickerViewManager::ReactContext(IReactContext reactContext) noexcept { 35 | m_reactContext = reactContext; 36 | } 37 | 38 | // IViewManagerWithNativeProperties 39 | IMapView TimePickerViewManager::NativeProps() noexcept { 40 | auto nativeProps = winrt::single_threaded_map(); 41 | nativeProps.Insert(L"selectedTime", ViewManagerPropertyType::Number); 42 | nativeProps.Insert(L"is24Hour", ViewManagerPropertyType::Boolean); 43 | nativeProps.Insert(L"minuteInterval", ViewManagerPropertyType::Number); 44 | 45 | return nativeProps.GetView(); 46 | } 47 | 48 | void TimePickerViewManager::UpdateProperties(xaml::FrameworkElement const& view, 49 | IJSValueReader const& propertyMapReader) noexcept { 50 | if (auto timePickerView = view.try_as()) { 51 | timePickerView->UpdateProperties(propertyMapReader); 52 | } 53 | else { 54 | OutputDebugStringW(L"Type deduction for TimePickerView failed."); 55 | } 56 | } 57 | 58 | // IViewManagerWithExportedEventTypeConstants 59 | ConstantProviderDelegate TimePickerViewManager::ExportedCustomBubblingEventTypeConstants() noexcept { 60 | return nullptr; 61 | } 62 | 63 | ConstantProviderDelegate TimePickerViewManager::ExportedCustomDirectEventTypeConstants() noexcept { 64 | return [](winrt::IJSValueWriter const& constantWriter) { 65 | WriteCustomDirectEventTypeConstant(constantWriter, "onChange"); 66 | }; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /ios/RNDateTimePickerShadowView.m: -------------------------------------------------------------------------------- 1 | #import "RNDateTimePickerShadowView.h" 2 | 3 | @implementation RNDateTimePickerShadowView 4 | 5 | - (instancetype)init 6 | { 7 | if (self = [super init]) { 8 | YGNodeSetMeasureFunc(self.yogaNode, RNDateTimePickerShadowViewMeasure); 9 | } 10 | return self; 11 | } 12 | 13 | - (void)setDate:(NSDate *)date { 14 | _date = date; 15 | YGNodeMarkDirty(self.yogaNode); 16 | } 17 | 18 | - (void)setLocale:(NSLocale *)locale { 19 | _locale = locale; 20 | YGNodeMarkDirty(self.yogaNode); 21 | } 22 | 23 | - (void)setMode:(UIDatePickerMode)mode { 24 | _mode = mode; 25 | YGNodeMarkDirty(self.yogaNode); 26 | } 27 | 28 | 29 | - (void)setDisplayIOS:(UIDatePickerStyle)displayIOS { 30 | _displayIOS = displayIOS; 31 | YGNodeMarkDirty(self.yogaNode); 32 | } 33 | 34 | - (void)setTimeZoneOffsetInMinutes:(NSInteger)timeZoneOffsetInMinutes { 35 | _timeZoneOffsetInMinutes = timeZoneOffsetInMinutes; 36 | YGNodeMarkDirty(self.yogaNode); 37 | } 38 | 39 | - (void)setTimeZoneName:(NSString *)timeZoneName { 40 | _timeZoneName = timeZoneName; 41 | YGNodeMarkDirty(self.yogaNode); 42 | } 43 | 44 | static YGSize RNDateTimePickerShadowViewMeasure(YGNodeConstRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode) 45 | { 46 | RNDateTimePickerShadowView *shadowPickerView = (__bridge RNDateTimePickerShadowView *)YGNodeGetContext(node); 47 | 48 | __block CGSize size; 49 | dispatch_sync(dispatch_get_main_queue(), ^{ 50 | [shadowPickerView.picker setDate:shadowPickerView.date]; 51 | [shadowPickerView.picker setDatePickerMode:shadowPickerView.mode]; 52 | [shadowPickerView.picker setLocale:shadowPickerView.locale]; 53 | [shadowPickerView.picker setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:shadowPickerView.timeZoneOffsetInMinutes * 60]]; 54 | 55 | if (shadowPickerView.timeZoneName) { 56 | NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:shadowPickerView.timeZoneName]; 57 | if (timeZone != nil) { 58 | [shadowPickerView.picker setTimeZone:timeZone]; 59 | } else { 60 | RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames. Falling back to localTimeZone=%@", shadowPickerView.timeZoneName, NSTimeZone.localTimeZone.name); 61 | [shadowPickerView.picker setTimeZone:NSTimeZone.localTimeZone]; 62 | } 63 | } else { 64 | [shadowPickerView.picker setTimeZone:NSTimeZone.localTimeZone]; 65 | } 66 | 67 | if (@available(iOS 14.0, *)) { 68 | [shadowPickerView.picker setPreferredDatePickerStyle:shadowPickerView.displayIOS]; 69 | } 70 | 71 | size = [shadowPickerView.picker sizeThatFits:UILayoutFittingCompressedSize]; 72 | size.width += 10; 73 | }); 74 | 75 | return (YGSize){ 76 | RCTYogaFloatFromCoreGraphicsFloat(size.width), 77 | RCTYogaFloatFromCoreGraphicsFloat(size.height) 78 | }; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /.detoxrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testRunner: { 3 | $0: 'jest', 4 | args: { 5 | config: 'example/e2e/jest.config.js', 6 | }, 7 | }, 8 | devices: { 9 | simulator: { 10 | type: 'ios.simulator', 11 | device: { 12 | type: 'iPhone 15 Pro Max', 13 | }, 14 | }, 15 | emulator: { 16 | type: 'android.emulator', 17 | utilBinaryPaths: ['example/e2e/test-butler-app.apk'], 18 | device: { 19 | avdName: 'TestingAVD', 20 | }, 21 | }, 22 | 'android.attached': { 23 | type: 'android.attached', 24 | device: { 25 | adbName: '34HDU19716000753', 26 | }, 27 | }, 28 | }, 29 | apps: { 30 | 'ios.debug': { 31 | type: 'ios.app', 32 | binaryPath: 33 | 'example/ios/build/Build/Products/Debug-iphonesimulator/ReactTestApp.app', 34 | build: 35 | "export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace example/ios/date-time-picker-example.xcworkspace -destination 'platform=iOS Simulator,name=iPhone 14' -scheme date-time-picker-example -configuration Debug -derivedDataPath example/ios/build -UseModernBuildSystem=YES CODE_SIGNING_ALLOWED=NO", 36 | }, 37 | 'ios.release': { 38 | type: 'ios.app', 39 | binaryPath: 40 | 'example/ios/build/Build/Products/Release-iphonesimulator/ReactTestApp.app', 41 | build: 42 | 'export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace example/ios/date-time-picker-example.xcworkspace -sdk iphonesimulator -scheme date-time-picker-example -configuration Release -derivedDataPath example/ios/build -UseModernBuildSystem=YES CODE_SIGNING_ALLOWED=NO', 43 | }, 44 | 'android.debug': { 45 | type: 'android.apk', 46 | binaryPath: 'example/android/app/build/outputs/apk/debug/app-debug.apk', 47 | build: 48 | 'export RCT_NO_LAUNCH_PACKAGER=true && (cd example/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug)', 49 | }, 50 | 'android.release': { 51 | type: 'android.apk', 52 | binaryPath: 53 | 'example/android/app/build/outputs/apk/release/app-release.apk', 54 | build: 55 | 'export RCT_NO_LAUNCH_PACKAGER=true && (cd example/android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release)', 56 | }, 57 | }, 58 | configurations: { 59 | 'ios.sim.debug': { 60 | app: 'ios.debug', 61 | device: 'simulator', 62 | }, 63 | 'ios.sim.release': { 64 | app: 'ios.release', 65 | device: 'simulator', 66 | }, 67 | 'android.emu.debug': { 68 | app: 'android.debug', 69 | device: 'emulator', 70 | }, 71 | 'android.device.debug': { 72 | app: 'android.debug', 73 | device: 'android.attached', 74 | }, 75 | 'android.emu.release': { 76 | app: 'android.release', 77 | device: 'emulator', 78 | }, 79 | }, 80 | artifacts: { 81 | plugins: { 82 | video: { 83 | android: { 84 | size: [1280, 720], 85 | }, 86 | }, 87 | }, 88 | }, 89 | }; 90 | -------------------------------------------------------------------------------- /test/utils.test.js: -------------------------------------------------------------------------------- 1 | import {toMilliseconds, sharedPropsValidation} from '../src/utils.js'; 2 | 3 | describe('utils', () => { 4 | describe('toMilliseconds', () => { 5 | it('converts Date values by key to milliseconds', () => { 6 | const options = { 7 | value: new Date('2020-12-12'), 8 | minimumDate: new Date('1950-01-01'), 9 | maximumDate: new Date('2050-12-31'), 10 | }; 11 | 12 | toMilliseconds(options, 'value'); 13 | expect(options).toHaveProperty('value', 1607731200000); 14 | 15 | toMilliseconds(options, 'minimumDate', 'maximumDate'); 16 | expect(options).toHaveProperty('minimumDate', -631152000000); 17 | expect(options).toHaveProperty('maximumDate', 2556057600000); 18 | }); 19 | }); 20 | 21 | describe('sharedPropsValidation', () => { 22 | describe('minimumDate and maximumDate validation', () => { 23 | it('should not throw when dates are in correct order', () => { 24 | const value = new Date('2023-06-15'); 25 | const minimumDate = new Date('2023-01-01'); 26 | const maximumDate = new Date('2023-12-31'); 27 | 28 | expect(() => { 29 | sharedPropsValidation({value, minimumDate, maximumDate}); 30 | }).not.toThrow(); 31 | }); 32 | 33 | it('should not throw when dates are equal', () => { 34 | const value = new Date('2023-06-15'); 35 | const minimumDate = new Date('2023-06-15'); 36 | const maximumDate = new Date('2023-06-15'); 37 | 38 | expect(() => { 39 | sharedPropsValidation({value, minimumDate, maximumDate}); 40 | }).not.toThrow(); 41 | }); 42 | 43 | it('should not throw when only minimumDate is provided', () => { 44 | const value = new Date('2023-06-15'); 45 | const minimumDate = new Date('2023-01-01'); 46 | 47 | expect(() => { 48 | sharedPropsValidation({value, minimumDate}); 49 | }).not.toThrow(); 50 | }); 51 | 52 | it('should not throw when only maximumDate is provided', () => { 53 | const value = new Date('2023-06-15'); 54 | const maximumDate = new Date('2023-12-31'); 55 | 56 | expect(() => { 57 | sharedPropsValidation({value, maximumDate}); 58 | }).not.toThrow(); 59 | }); 60 | 61 | it('should not throw when neither minimumDate nor maximumDate is provided', () => { 62 | const value = new Date('2023-06-15'); 63 | 64 | expect(() => { 65 | sharedPropsValidation({value}); 66 | }).not.toThrow(); 67 | }); 68 | 69 | it('should throw when minimumDate is after maximumDate', () => { 70 | const value = new Date('2023-06-15'); 71 | const minimumDate = new Date('2023-12-31'); 72 | const maximumDate = new Date('2023-01-01'); 73 | 74 | expect(() => { 75 | sharedPropsValidation({value, minimumDate, maximumDate}); 76 | }).toThrow('DateTimePicker: minimumDate (2023-12-31T00:00:00.000Z) is after maximumDate (2023-01-01T00:00:00.000Z). Ensure minimumDate < maximumDate.'); 77 | }); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactcommunity/rndatetimepicker/RNDateTimePickerPackage.java: -------------------------------------------------------------------------------- 1 | package com.reactcommunity.rndatetimepicker; 2 | 3 | 4 | import androidx.annotation.Nullable; 5 | 6 | import com.facebook.react.BaseReactPackage; 7 | import com.facebook.react.bridge.NativeModule; 8 | import com.facebook.react.bridge.ReactApplicationContext; 9 | import com.facebook.react.module.model.ReactModuleInfo; 10 | import com.facebook.react.module.model.ReactModuleInfoProvider; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | public class RNDateTimePickerPackage extends BaseReactPackage { 16 | @Nullable 17 | @Override 18 | public NativeModule getModule(String name, ReactApplicationContext reactContext) { 19 | if (name.equals(DatePickerModule.NAME)) { 20 | return new DatePickerModule(reactContext); 21 | } else if (name.equals(TimePickerModule.NAME)) { 22 | return new TimePickerModule(reactContext); 23 | } else if (name.equals(MaterialDatePickerModule.NAME)) { 24 | return new MaterialDatePickerModule(reactContext); 25 | } else if (name.equals(MaterialTimePickerModule.NAME)) { 26 | return new MaterialTimePickerModule(reactContext); 27 | } else { 28 | return null; 29 | } 30 | } 31 | 32 | @Override 33 | public ReactModuleInfoProvider getReactModuleInfoProvider() { 34 | return () -> { 35 | boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; 36 | final Map moduleInfos = new HashMap<>(); 37 | moduleInfos.put( 38 | DatePickerModule.NAME, 39 | new ReactModuleInfo( 40 | DatePickerModule.NAME, 41 | DatePickerModule.NAME, 42 | false, // canOverrideExistingModule 43 | false, // needsEagerInit 44 | false, // isCxxModule 45 | isTurboModule // isTurboModule 46 | )); 47 | moduleInfos.put( 48 | TimePickerModule.NAME, 49 | new ReactModuleInfo( 50 | TimePickerModule.NAME, 51 | TimePickerModule.NAME, 52 | false, // canOverrideExistingModule 53 | false, // needsEagerInit 54 | false, // isCxxModule 55 | isTurboModule // isTurboModule 56 | )); 57 | moduleInfos.put( 58 | MaterialDatePickerModule.NAME, 59 | new ReactModuleInfo( 60 | MaterialDatePickerModule.NAME, 61 | MaterialDatePickerModule.NAME, 62 | false, // canOverrideExistingModule 63 | false, // needsEagerInit 64 | false, // hasConstants 65 | false, // isCxxModule 66 | isTurboModule // isTurboModule 67 | )); 68 | moduleInfos.put( 69 | MaterialTimePickerModule.NAME, 70 | new ReactModuleInfo( 71 | MaterialTimePickerModule.NAME, 72 | MaterialTimePickerModule.NAME, 73 | false, // canOverrideExistingModule 74 | false, // needsEagerInit 75 | false, // hasConstants 76 | false, // isCxxModule 77 | isTurboModule // isTurboModule 78 | )); 79 | return moduleInfos; 80 | }; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /windows/DateTimePickerWindows/DateTimePickerViewManager.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | #include "pch.h" 5 | #include "DateTimePickerViewManager.h" 6 | #include "NativeModules.h" 7 | #include "DateTimePickerView.h" 8 | 9 | namespace winrt { 10 | using namespace Microsoft::ReactNative; 11 | using namespace Windows::Foundation::Collections; 12 | 13 | namespace xaml = winrt::Windows::UI::Xaml; 14 | } 15 | 16 | namespace winrt::DateTimePicker::implementation { 17 | 18 | DateTimePickerViewManager::DateTimePickerViewManager() {} 19 | 20 | // IViewManager 21 | winrt::hstring DateTimePickerViewManager::Name() noexcept { 22 | return L"RNDateTimePickerWindows"; 23 | } 24 | 25 | xaml::FrameworkElement DateTimePickerViewManager::CreateView() noexcept { 26 | return winrt::DateTimePicker::DateTimePickerView(m_reactContext); 27 | } 28 | 29 | // IViewManagerWithReactContext 30 | winrt::IReactContext DateTimePickerViewManager::ReactContext() noexcept { 31 | return m_reactContext; 32 | } 33 | 34 | void DateTimePickerViewManager::ReactContext(IReactContext reactContext) noexcept { 35 | m_reactContext = reactContext; 36 | } 37 | 38 | // IViewManagerWithNativeProperties 39 | IMapView DateTimePickerViewManager::NativeProps() noexcept { 40 | auto nativeProps = winrt::single_threaded_map(); 41 | 42 | nativeProps.Insert(L"accessibilityLabel", ViewManagerPropertyType::String); 43 | nativeProps.Insert(L"dayOfWeekFormat", ViewManagerPropertyType::String); 44 | nativeProps.Insert(L"dateFormat", ViewManagerPropertyType::String); 45 | nativeProps.Insert(L"firstDayOfWeek", ViewManagerPropertyType::Number); 46 | nativeProps.Insert(L"maxDate", ViewManagerPropertyType::Number); 47 | nativeProps.Insert(L"minDate", ViewManagerPropertyType::Number); 48 | nativeProps.Insert(L"placeholderText", ViewManagerPropertyType::String); 49 | nativeProps.Insert(L"selectedDate", ViewManagerPropertyType::Number); 50 | nativeProps.Insert(L"timeZoneOffsetInSeconds", ViewManagerPropertyType::Number); 51 | 52 | return nativeProps.GetView(); 53 | } 54 | 55 | void DateTimePickerViewManager::UpdateProperties(xaml::FrameworkElement const& view, 56 | IJSValueReader const& propertyMapReader) noexcept { 57 | if (auto dateTimePickerView = view.try_as()) { 58 | dateTimePickerView->UpdateProperties(propertyMapReader); 59 | } else { 60 | OutputDebugStringW(L"Type deduction for DateTimePickerView failed."); 61 | } 62 | } 63 | 64 | // IViewManagerWithExportedEventTypeConstants 65 | ConstantProviderDelegate DateTimePickerViewManager::ExportedCustomBubblingEventTypeConstants() noexcept { 66 | return nullptr; 67 | } 68 | 69 | ConstantProviderDelegate DateTimePickerViewManager::ExportedCustomDirectEventTypeConstants() noexcept { 70 | return [](winrt::IJSValueWriter const& constantWriter) { 71 | WriteCustomDirectEventTypeConstant(constantWriter, "onChange"); 72 | }; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | apply(from: { 3 | def searchDir = rootDir.toPath() 4 | do { 5 | def p = searchDir.resolve("node_modules/react-native-test-app/android/dependencies.gradle") 6 | if (p.toFile().exists()) { 7 | return p.toRealPath().toString() 8 | } 9 | } while (searchDir = searchDir.getParent()) 10 | throw new GradleException("Could not find `react-native-test-app`"); 11 | }()) 12 | 13 | repositories { 14 | mavenCentral() 15 | google() 16 | } 17 | 18 | dependencies { 19 | getReactNativeDependencies().each { dependency -> 20 | classpath(dependency) 21 | } 22 | } 23 | } 24 | 25 | allprojects { 26 | repositories { 27 | maven { 28 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 29 | url({ 30 | def searchDir = rootDir.toPath() 31 | do { 32 | def p = searchDir.resolve("node_modules/react-native/android") 33 | if (p.toFile().exists()) { 34 | return p.toRealPath().toString() 35 | } 36 | } while (searchDir = searchDir.getParent()) 37 | throw new GradleException("Could not find `react-native`"); 38 | }()) 39 | } 40 | maven { 41 | // All of Detox' artifacts are provided via the npm module 42 | url "$rootDir/../../node_modules/detox/Detox-android" 43 | } 44 | mavenCentral() 45 | google() 46 | } 47 | // https://github.com/microsoft/react-native-test-app/wiki/Platform-Specifics:-Android#detox 48 | afterEvaluate { project -> 49 | def androidExtension = project.extensions.findByName('android') 50 | if (androidExtension != null && project.name == 'app') { 51 | androidExtension.defaultConfig { 52 | ndk { 53 | abiFilters 'arm64-v8a', 'x86', 'x86_64' 54 | } 55 | // this along with `androidTestImplementation`s fixes 56 | // Opening an oat file without a class loader. Are you using the deprecated DexFile APIs? 57 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 58 | } 59 | 60 | // needed for release signing 61 | androidExtension.signingConfigs { 62 | test { 63 | keyAlias "androiddebugkey" 64 | keyPassword "android" 65 | storeFile file("debug.keystore") 66 | storePassword "android" 67 | } 68 | } 69 | 70 | androidExtension.buildTypes.debug.signingConfig = androidExtension.signingConfigs.test 71 | androidExtension.buildTypes.release.signingConfig = androidExtension.signingConfigs.test 72 | androidExtension.testBuildType = System.getProperty('testBuildType', 'debug') 73 | // end needed for release signing 74 | 75 | androidExtension.sourceSets.androidTest.java.srcDirs += "$rootDir/app/src/androidTest/java" 76 | 77 | project.dependencies { 78 | androidTestImplementation('com.wix:detox:+') 79 | androidTestImplementation 'androidx.test:runner:1.5.2' 80 | androidTestImplementation 'androidx.test:rules:1.5.2' 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /docs/android-styling.md: -------------------------------------------------------------------------------- 1 | ## Android Styling 2 | 3 | To use the features documented here, you need to use version >= `8.2.0` and a [Expo Development build](https://docs.expo.dev/develop/development-builds/introduction/). 4 | 5 | Make changes as documented below and then run the following commands to see the updated colors: 6 | 7 | - `npx expo prebuild -p android --clean` (apply configuration to the native code) 8 | - `expo run:android` (build the native code) 9 | 10 | ### Configuration in app.json / app.config.js 11 | 12 | ```json 13 | { 14 | "expo": { 15 | "plugins": [ 16 | [ 17 | "@react-native-community/datetimepicker", 18 | { 19 | "android": { 20 | "datePicker": { 21 | "colorAccent": { 22 | "light": "#FF5722" 23 | }, 24 | "textColorPrimary": { 25 | "light": "#FF5722" 26 | } 27 | }, 28 | "timePicker": { 29 | "background": {"light": "#FF5722", "dark": "#383838"}, 30 | "numbersBackgroundColor": {"light": "#FF5722", "dark": "#383838"} 31 | } 32 | } 33 | } 34 | ] 35 | ] 36 | } 37 | } 38 | ``` 39 | 40 | It's not possible to specify a color only for dark mode. If you wish to influence dark mode color you must declare a value for both the `light` and `dark` modes. Plugin will throw an error otherwise. Plugin also validates that the color names you specify (e.g. `textColorPrimary`) are valid. 41 | 42 | ### Configurable properties 43 | 44 | The following illustrations show the different styles that can be applied to the date and time pickers. 45 | 46 | | DatePickerDialog | TimePickerDialog | 47 | | -------------------------------------------------------------------------- | ------------------------------------------------------------ | 48 | | ![Date picker dialog breakdown](./images/date_picker_dialog_breakdown.png) | ![Time picker breakdown](./images/time_picker_breakdown.png) | 49 | 50 | #### DatePickerDialog 51 | 52 | | Property | Attribute Name | 53 | | ---------------------------------- | ------------------------------------------ | 54 | | colorAccent | colorAccent | 55 | | colorControlActivated | colorControlActivated | 56 | | colorControlHighlight | colorControlHighlight | 57 | | selectableItemBackgroundBorderless | android:selectableItemBackgroundBorderless | 58 | | textColor | android:textColor | 59 | | textColorPrimary | android:textColorPrimary | 60 | | textColorPrimaryInverse | android:textColorPrimaryInverse | 61 | | textColorSecondary | android:textColorSecondary | 62 | | textColorSecondaryInverse | android:textColorSecondaryInverse | 63 | | windowBackground | android:windowBackground | 64 | 65 | #### TimePickerDialog 66 | 67 | | Property | Attribute Name | 68 | | ---------------------- | ------------------------------ | 69 | | background | android:background | 70 | | headerBackground | android:headerBackground | 71 | | numbersBackgroundColor | android:numbersBackgroundColor | 72 | | numbersSelectorColor | android:numbersSelectorColor | 73 | | numbersTextColor | android:numbersTextColor | 74 | -------------------------------------------------------------------------------- /test/userlandTestExamples.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @flow strict-local 4 | */ 5 | import React, {useState} from 'react'; 6 | import {Text, Button} from 'react-native'; 7 | // in your code, import from '@react-native-community/datetimepicker' 8 | import DateTimePicker from '../src/index'; 9 | // $FlowExpectedError: complains about import path 10 | import {DateTimePickerAndroid} from '../src/DateTimePickerAndroid.android'; 11 | 12 | // $FlowExpectedError: module treated as any 13 | import {render, fireEvent, waitFor} from '@testing-library/react-native'; 14 | import {createDateTimeSetEvtParams} from '../src/index'; 15 | import {mockAndroidDialogDateChange, mockAndroidDialogDismissal} from '../jest'; 16 | 17 | function TestAppWithComponent() { 18 | const [date, setDate] = React.useState(); 19 | 20 | return ( 21 | <> 22 | { 25 | setDate(selectedDate); 26 | }} 27 | /> 28 | {String(date?.toLocaleString())} 29 | {String((date?.getTime() ?? 0) / 1000)} 30 | 31 | ); 32 | } 33 | 34 | const AppWithImperativePicker = () => { 35 | const [date, setDate] = useState(new Date(0)); 36 | 37 | const onChange = (event, selectedDate) => { 38 | selectedDate && setDate(selectedDate); 39 | }; 40 | 41 | const showMode = (currentMode) => { 42 | DateTimePickerAndroid.open({ 43 | value: date, 44 | onChange, 45 | display: 'default', 46 | mode: currentMode, 47 | is24Hour: true, 48 | }); 49 | }; 50 | 51 | const showDatepicker = () => { 52 | showMode('date'); 53 | }; 54 | 55 | const time = date?.getTime(); 56 | return ( 57 | <> 58 |