nx-react-native-cli 2.7.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.cjs +43 -43
- package/package.json +1 -1
- package/templates/19.7.0/apps/mobile/android/app/src/main/java/com/appsmobile/MainActivity.kt +5 -1
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/AppDelegate.mm +6 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-20@2x.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-20@2x~ipad.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-20@3x.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-20~ipad.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-29.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-29@2x.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-29@2x~ipad.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-29@3x.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-29~ipad.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-40@2x.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-40@2x~ipad.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-40@3x.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-40~ipad.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-60@2x~car.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-60@3x~car.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-83.5@2x~ipad.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon@2x.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon@3x.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon~ios-marketing.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon~ipad.png +0 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/Contents.json +134 -0
- package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/Contents.json +6 -0
- package/templates/21.2.2/apps/mobile/android/app/src/main/java/com/mobile/MainActivity.kt +5 -1
- package/templates/21.2.2/apps/mobile/ios/Mobile/AppDelegate.mm +6 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-20@2x.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-20@2x~ipad.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-20@3x.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-20~ipad.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-29.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-29@2x.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-29@2x~ipad.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-29@3x.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-29~ipad.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-40@2x.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-40@2x~ipad.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-40@3x.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-40~ipad.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-60@2x~car.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-60@3x~car.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-83.5@2x~ipad.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon@2x.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon@3x.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon~ios-marketing.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon~ipad.png +0 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/Contents.json +134 -0
- package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/Contents.json +6 -0
- package/templates/shared/apps/mobile/android/app/src/main/AndroidManifest.xml +28 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png +0 -0
- package/templates/shared/apps/mobile/android/app/src/main/res/styles.xml +14 -0
- package/templates/shared/apps/mobile/run-android.sh +2 -1
- package/templates/shared/apps/mobile/scripts/setup-ios-dev-scheme.rb +101 -1
- package/templates/shared/apps/mobile/src/app/index.tsx +16 -47
- package/templates/shared/apps/mobile/src/app/query-client.ts +40 -0
- package/templates/shared/apps/mobile/src/assets/images/logo.png +0 -0
- package/templates/shared/apps/mobile/src/components/atoms/AlertManager/alert-manager.component.tsx +134 -0
- package/templates/shared/apps/mobile/src/components/atoms/AlertManager/alert-manager.types.ts +18 -0
- package/templates/shared/apps/mobile/src/components/atoms/AlertManager/alert.service.ts +27 -0
- package/templates/shared/apps/mobile/src/components/atoms/AlertManager/index.ts +3 -0
- package/templates/shared/apps/mobile/src/components/atoms/BottomSheet/bottom-sheet.component.tsx +14 -8
- package/templates/shared/apps/mobile/src/components/atoms/Button/button.component.tsx +1 -1
- package/templates/shared/apps/mobile/src/components/atoms/DateModalInput/date-modal-input.component.tsx +69 -0
- package/templates/shared/apps/mobile/src/components/atoms/DateModalInput/index.ts +1 -0
- package/templates/shared/apps/mobile/src/components/atoms/DatePicker/date-picker.component.tsx +44 -0
- package/templates/shared/apps/mobile/src/components/atoms/DatePicker/index.ts +1 -0
- package/templates/shared/apps/mobile/src/components/atoms/DateTextInput/date-text-input.component.tsx +218 -0
- package/templates/shared/apps/mobile/src/components/atoms/DateTextInput/index.ts +1 -0
- package/templates/shared/apps/mobile/src/components/atoms/Divider/divider-component.tsx +1 -1
- package/templates/shared/apps/mobile/src/components/atoms/GradientBackground/gradient-background.component.tsx +45 -0
- package/templates/shared/apps/mobile/src/components/atoms/GradientBackground/index.ts +1 -0
- package/templates/shared/apps/mobile/src/components/atoms/InputLayout/input-layout.component.tsx +12 -4
- package/templates/shared/apps/mobile/src/components/atoms/KeyboardAccessory/keyboard-accessory.component.tsx +6 -3
- package/templates/shared/apps/mobile/src/components/atoms/KeyboardAwareScrollView/keyboard-aware-scroll-view.component.tsx +1 -0
- package/templates/shared/apps/mobile/src/components/atoms/Modal/modal.component.tsx +2 -0
- package/templates/shared/apps/mobile/src/components/atoms/ScreenLoader/screen-loader.component.tsx +6 -1
- package/templates/shared/apps/mobile/src/components/atoms/SelectDropdown/index.ts +1 -0
- package/templates/shared/apps/mobile/src/components/atoms/SelectDropdown/select-dropdown.component.tsx +223 -0
- package/templates/shared/apps/mobile/src/components/atoms/Skeleton/skeleton.component.tsx +1 -1
- package/templates/shared/apps/mobile/src/components/atoms/TextInput/bottom-sheet-text-input.component.tsx +4 -3
- package/templates/shared/apps/mobile/src/components/atoms/TextInput/text-input.component.tsx +8 -4
- package/templates/shared/apps/mobile/src/components/atoms/ThemeManager/index.ts +1 -0
- package/templates/shared/apps/mobile/src/components/atoms/ThemeManager/theme-manager.component.tsx +27 -0
- package/templates/shared/apps/mobile/src/components/atoms/ToastManager/index.ts +3 -0
- package/templates/shared/apps/mobile/src/components/atoms/ToastManager/toast-manager.component.tsx +109 -0
- package/templates/shared/apps/mobile/src/components/atoms/ToastManager/toast-manager.types.ts +10 -0
- package/templates/shared/apps/mobile/src/components/atoms/ToastManager/toast.service.ts +27 -0
- package/templates/shared/apps/mobile/src/components/atoms/Typography/typography.component.tsx +1 -1
- package/templates/shared/apps/mobile/src/components/atoms/index.ts +8 -0
- package/templates/shared/apps/mobile/src/components/molecules/BackButton/back-button.component.tsx +1 -1
- package/templates/shared/apps/mobile/src/components/molecules/ScreenContainer/screen-container.component.tsx +4 -24
- package/templates/shared/apps/mobile/src/components/molecules/ScreenHeader/screen-header.component.tsx +2 -2
- package/templates/shared/apps/mobile/src/hooks/index.ts +1 -0
- package/templates/shared/apps/mobile/src/hooks/usePushNotifications.hook.ts +104 -0
- package/templates/shared/apps/mobile/src/hooks/useToggleDarkMode.hook.tsx +24 -2
- package/templates/shared/apps/mobile/src/icons/alert-triangle.svg +5 -0
- package/templates/shared/apps/mobile/src/icons/check-circle.svg +4 -0
- package/templates/shared/apps/mobile/src/icons/chevron-down.svg +1 -0
- package/templates/shared/apps/mobile/src/icons/chevron-right.svg +1 -0
- package/templates/shared/apps/mobile/src/icons/index.ts +18 -1
- package/templates/shared/apps/mobile/src/icons/info.svg +5 -0
- package/templates/shared/apps/mobile/src/icons/x-circle.svg +5 -0
- package/templates/shared/apps/mobile/src/routes/index.tsx +26 -15
- package/templates/shared/apps/mobile/src/screens/LandingScreen/landing.screen.tsx +232 -8
- package/templates/shared/apps/mobile/src/stores/local-storage.store.ts +9 -5
- package/templates/shared/apps/mobile/src/stores/theme.slice.ts +15 -0
- package/templates/shared/apps/mobile/src/stores/user.slice.ts +5 -1
- package/templates/shared/apps/mobile/src/tailwind/index.ts +3 -3
- package/templates/shared/apps/mobile/tailwind.config.js +14 -0
- package/templates/shared/patches/react-native-animatable+1.4.0.patch +71 -0
- package/templates/shared/apps/mobile/src/assets/images/.gitkeep +0 -0
|
@@ -157,6 +157,24 @@ def upsert_info_plist_display_name(app_target, ios_dir, project_name)
|
|
|
157
157
|
updated
|
|
158
158
|
end
|
|
159
159
|
|
|
160
|
+
def upsert_info_plist_encryption_flag(app_target, ios_dir, project_name)
|
|
161
|
+
paths = resolve_info_plist_paths(app_target, ios_dir, project_name)
|
|
162
|
+
updated = []
|
|
163
|
+
|
|
164
|
+
paths.each do |path|
|
|
165
|
+
next unless File.exist?(path)
|
|
166
|
+
|
|
167
|
+
plist = Xcodeproj::Plist.read_from_path(path) || {}
|
|
168
|
+
next if plist['ITSAppUsesNonExemptEncryption'] == false
|
|
169
|
+
|
|
170
|
+
plist['ITSAppUsesNonExemptEncryption'] = false
|
|
171
|
+
Xcodeproj::Plist.write_to_path(plist, path)
|
|
172
|
+
updated << "Info.plist ITSAppUsesNonExemptEncryption=false at #{path}"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
updated
|
|
176
|
+
end
|
|
177
|
+
|
|
160
178
|
def update_scheme_configuration(doc, action_name, build_configuration)
|
|
161
179
|
action = REXML::XPath.first(doc, "//#{action_name}")
|
|
162
180
|
raise "Missing #{action_name} in source scheme." unless action
|
|
@@ -301,6 +319,81 @@ def upsert_firebase_environment_script_phase(app_target)
|
|
|
301
319
|
phase_status
|
|
302
320
|
end
|
|
303
321
|
|
|
322
|
+
def upsert_podfile_node_require_and_permissions(podfile_path)
|
|
323
|
+
return :missing unless File.exist?(podfile_path)
|
|
324
|
+
|
|
325
|
+
content = File.read(podfile_path)
|
|
326
|
+
updated = content.dup
|
|
327
|
+
changes = []
|
|
328
|
+
|
|
329
|
+
# Step 1: Replace old inline require with node_require function if needed
|
|
330
|
+
unless updated.include?('def node_require(script)')
|
|
331
|
+
old_require_pattern = /# Resolve react_native_pods\.rb with node to allow for hoisting\nrequire Pod::Executable\.execute_command\('node', \['-p',\n\s*'require\.resolve\(\n\s*"react-native\/scripts\/react_native_pods\.rb",\n\s*\{paths: \[process\.argv\[1\]\]\},\n\s*\)', __dir__\]\)\.strip/m
|
|
332
|
+
|
|
333
|
+
node_require_block = <<~RUBY.chomp
|
|
334
|
+
def node_require(script)
|
|
335
|
+
# Resolve script with node to allow for hoisting
|
|
336
|
+
require Pod::Executable.execute_command('node', ['-p',
|
|
337
|
+
"require.resolve(
|
|
338
|
+
'\#{script}',
|
|
339
|
+
{paths: [process.argv[1]]},
|
|
340
|
+
)", __dir__]).strip
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Use it to require both react-native's and this package's scripts:
|
|
344
|
+
node_require('react-native/scripts/react_native_pods.rb')
|
|
345
|
+
node_require('react-native-permissions/scripts/setup.rb')
|
|
346
|
+
RUBY
|
|
347
|
+
|
|
348
|
+
if updated.match?(old_require_pattern)
|
|
349
|
+
updated.sub!(old_require_pattern, node_require_block)
|
|
350
|
+
changes << 'node_require function (replaced inline require)'
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Step 2: Add setup_permissions after prepare_react_native_project! if not present
|
|
355
|
+
unless updated.include?('setup_permissions')
|
|
356
|
+
permissions_block = <<~RUBY
|
|
357
|
+
|
|
358
|
+
# Uncomment the permissions you need
|
|
359
|
+
setup_permissions([
|
|
360
|
+
# 'AppTrackingTransparency',
|
|
361
|
+
# 'Bluetooth',
|
|
362
|
+
# 'Calendars',
|
|
363
|
+
# 'CalendarsWriteOnly',
|
|
364
|
+
# 'Camera',
|
|
365
|
+
# 'Contacts',
|
|
366
|
+
# 'FaceID',
|
|
367
|
+
# 'LocationAccuracy',
|
|
368
|
+
# 'LocationAlways',
|
|
369
|
+
# 'LocationWhenInUse',
|
|
370
|
+
# 'MediaLibrary',
|
|
371
|
+
# 'Microphone',
|
|
372
|
+
# 'Motion',
|
|
373
|
+
'Notifications',
|
|
374
|
+
# 'PhotoLibrary',
|
|
375
|
+
# 'PhotoLibraryAddOnly',
|
|
376
|
+
# 'Reminders',
|
|
377
|
+
# 'Siri',
|
|
378
|
+
# 'SpeechRecognition',
|
|
379
|
+
# 'StoreKit',
|
|
380
|
+
])
|
|
381
|
+
RUBY
|
|
382
|
+
|
|
383
|
+
if updated.include?('prepare_react_native_project!')
|
|
384
|
+
updated.sub!(/prepare_react_native_project!\s*\n/) do |match|
|
|
385
|
+
"#{match}#{permissions_block}"
|
|
386
|
+
end
|
|
387
|
+
changes << 'setup_permissions block'
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
return :unchanged if updated == content
|
|
392
|
+
|
|
393
|
+
File.write(podfile_path, updated)
|
|
394
|
+
changes
|
|
395
|
+
end
|
|
396
|
+
|
|
304
397
|
ios_dir = File.join(Dir.pwd, 'ios')
|
|
305
398
|
project_path = Dir.glob(File.join(ios_dir, '*.xcodeproj')).first
|
|
306
399
|
raise 'Could not find an .xcodeproj under ios/.' unless project_path
|
|
@@ -330,6 +423,7 @@ changes.concat(upsert_product_bundle_identifier(app_target, base_bundle_identifi
|
|
|
330
423
|
base_product_name = extract_base_product_name(app_target)
|
|
331
424
|
changes.concat(upsert_product_name(app_target, base_product_name))
|
|
332
425
|
changes.concat(upsert_info_plist_display_name(app_target, ios_dir, project_name))
|
|
426
|
+
changes.concat(upsert_info_plist_encryption_flag(app_target, ios_dir, project_name))
|
|
333
427
|
firebase_phase_status = upsert_firebase_environment_script_phase(app_target)
|
|
334
428
|
changes << "Firebase plist run script #{firebase_phase_status}" unless firebase_phase_status == 'existing'
|
|
335
429
|
|
|
@@ -359,9 +453,15 @@ formatter.write(scheme_doc.root, output)
|
|
|
359
453
|
output << "\n"
|
|
360
454
|
File.write(dev_scheme_path, output)
|
|
361
455
|
|
|
362
|
-
|
|
456
|
+
podfile_path = File.join(ios_dir, 'Podfile')
|
|
457
|
+
podfile_status = upsert_podfile_project_mapping(podfile_path, project_name)
|
|
363
458
|
changes << 'Podfile project mapping' if podfile_status == :updated
|
|
364
459
|
|
|
460
|
+
node_require_status = upsert_podfile_node_require_and_permissions(podfile_path)
|
|
461
|
+
if node_require_status.is_a?(Array)
|
|
462
|
+
node_require_status.each { |change| changes << "Podfile #{change}" }
|
|
463
|
+
end
|
|
464
|
+
|
|
365
465
|
puts 'Configured iOS build configurations and Dev scheme.'
|
|
366
466
|
if changes.empty?
|
|
367
467
|
puts 'No new build configurations were created (already configured).'
|
|
@@ -1,69 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { QueryClient } from '@tanstack/react-query';
|
|
3
|
-
import { AsyncStorage, PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
|
|
1
|
+
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
|
|
4
2
|
import React from 'react';
|
|
5
3
|
import { LogBox } from 'react-native';
|
|
6
4
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
7
5
|
import 'react-native-get-random-values';
|
|
8
6
|
import { KeyboardProvider } from 'react-native-keyboard-controller';
|
|
9
|
-
import { createMMKV } from 'react-native-mmkv';
|
|
10
7
|
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
11
|
-
import { useDeviceContext } from 'twrnc';
|
|
12
8
|
|
|
13
|
-
import {
|
|
14
|
-
|
|
9
|
+
import { persistOptions, queryClient } from './query-client';
|
|
10
|
+
|
|
11
|
+
import { AlertManager, StorageManager, ThemeManager, ToastManager } from '@/components';
|
|
15
12
|
import ApplicationRoutes from '@/routes';
|
|
16
13
|
import { tw } from '@/tailwind';
|
|
17
14
|
import 'react-native-url-polyfill/auto';
|
|
18
15
|
|
|
19
|
-
LogBox.ignoreLogs([
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
defaultOptions: {
|
|
26
|
-
queries: {
|
|
27
|
-
staleTime: STALE_TIME,
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const storage = createMMKV({
|
|
33
|
-
encryptionKey: CONFIG.STORAGE_KEY,
|
|
34
|
-
id: 'react-query-persist',
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
export const MmkvStorage: AsyncStorage = {
|
|
38
|
-
getItem: (name) => {
|
|
39
|
-
const value = storage.getString(name);
|
|
40
|
-
|
|
41
|
-
return value ?? null;
|
|
42
|
-
},
|
|
43
|
-
removeItem: (name) => {
|
|
44
|
-
storage.remove(name);
|
|
45
|
-
},
|
|
46
|
-
setItem: (name, value) => storage.set(name, value),
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const persister = createAsyncStoragePersister({
|
|
50
|
-
storage: MmkvStorage as AsyncStorage,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const persistOptions = { maxAge: CACHE_TIME, persister };
|
|
16
|
+
LogBox.ignoreLogs([
|
|
17
|
+
'VirtualizedLists',
|
|
18
|
+
'onAnimatedValueUpdate',
|
|
19
|
+
'InteractionManager',
|
|
20
|
+
'This method is deprecated (as well as all React Native Firebase namespaced API)',
|
|
21
|
+
]);
|
|
54
22
|
|
|
55
23
|
function Application() {
|
|
56
|
-
useDeviceContext(tw, {
|
|
57
|
-
initialColorScheme: 'light',
|
|
58
|
-
});
|
|
59
|
-
|
|
60
24
|
return (
|
|
61
25
|
<GestureHandlerRootView style={tw`flex-1`}>
|
|
62
26
|
<SafeAreaProvider>
|
|
63
27
|
<KeyboardProvider navigationBarTranslucent statusBarTranslucent>
|
|
64
28
|
<PersistQueryClientProvider client={queryClient} persistOptions={persistOptions}>
|
|
65
29
|
<StorageManager>
|
|
66
|
-
|
|
30
|
+
<>
|
|
31
|
+
<ThemeManager />
|
|
32
|
+
<ApplicationRoutes />
|
|
33
|
+
<AlertManager />
|
|
34
|
+
<ToastManager />
|
|
35
|
+
</>
|
|
67
36
|
</StorageManager>
|
|
68
37
|
</PersistQueryClientProvider>
|
|
69
38
|
</KeyboardProvider>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister';
|
|
2
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
3
|
+
import { AsyncStorage } from '@tanstack/react-query-persist-client';
|
|
4
|
+
import { createMMKV } from 'react-native-mmkv';
|
|
5
|
+
|
|
6
|
+
import CONFIG from '@/config';
|
|
7
|
+
|
|
8
|
+
const CACHE_TIME = 0;
|
|
9
|
+
const STALE_TIME = 0;
|
|
10
|
+
|
|
11
|
+
export const queryClient = new QueryClient({
|
|
12
|
+
defaultOptions: {
|
|
13
|
+
queries: {
|
|
14
|
+
staleTime: STALE_TIME,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const storage = createMMKV({
|
|
20
|
+
encryptionKey: CONFIG.STORAGE_KEY,
|
|
21
|
+
id: 'react-query-persist',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const MmkvStorage: AsyncStorage = {
|
|
25
|
+
getItem: (name) => {
|
|
26
|
+
const value = storage.getString(name);
|
|
27
|
+
|
|
28
|
+
return value ?? null;
|
|
29
|
+
},
|
|
30
|
+
removeItem: (name) => {
|
|
31
|
+
storage.remove(name);
|
|
32
|
+
},
|
|
33
|
+
setItem: (name, value) => storage.set(name, value),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const persister = createAsyncStoragePersister({
|
|
37
|
+
storage: MmkvStorage as AsyncStorage,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export const persistOptions = { maxAge: CACHE_TIME, persister };
|
|
Binary file
|
package/templates/shared/apps/mobile/src/components/atoms/AlertManager/alert-manager.component.tsx
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import React, { useCallback, useImperativeHandle, useRef, useState } from 'react';
|
|
2
|
+
import { Pressable, View } from 'react-native';
|
|
3
|
+
import { SvgProps } from 'react-native-svg';
|
|
4
|
+
|
|
5
|
+
import { AlertManagerRef, AlertOptions, AlertVariant } from './alert-manager.types';
|
|
6
|
+
import { Alert } from './alert.service';
|
|
7
|
+
|
|
8
|
+
import { Modal } from '@/components/atoms';
|
|
9
|
+
import { Typography } from '@/components/atoms/Typography';
|
|
10
|
+
import { AlertTriangleIcon, CheckCircleIcon, InfoIcon, XCircleIcon } from '@/icons';
|
|
11
|
+
import { colors, tw } from '@/tailwind';
|
|
12
|
+
|
|
13
|
+
type QueuedAlert = AlertOptions & {
|
|
14
|
+
variant: AlertVariant;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type VariantConfig = {
|
|
18
|
+
color: string;
|
|
19
|
+
icon: React.FC<SvgProps>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const ICON_SIZE = 24;
|
|
23
|
+
|
|
24
|
+
const VARIANT_CONFIG: Record<AlertVariant, VariantConfig> = {
|
|
25
|
+
error: { color: colors.error, icon: XCircleIcon },
|
|
26
|
+
info: { color: colors.secondary[500], icon: InfoIcon },
|
|
27
|
+
success: { color: colors.success, icon: CheckCircleIcon },
|
|
28
|
+
warning: { color: colors.primary[500], icon: AlertTriangleIcon },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function AlertManager() {
|
|
32
|
+
const [queue, setQueue] = useState<QueuedAlert[]>([]);
|
|
33
|
+
const ref = useRef<AlertManagerRef>(null);
|
|
34
|
+
|
|
35
|
+
const currentAlert = queue[0];
|
|
36
|
+
const isVisible = !!currentAlert;
|
|
37
|
+
|
|
38
|
+
const handleDismiss = useCallback(() => {
|
|
39
|
+
setQueue((prev) => prev.slice(1));
|
|
40
|
+
}, []);
|
|
41
|
+
|
|
42
|
+
const handleActionPress = useCallback(
|
|
43
|
+
(onPress?: () => void) => {
|
|
44
|
+
handleDismiss();
|
|
45
|
+
onPress?.();
|
|
46
|
+
},
|
|
47
|
+
[handleDismiss],
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
useImperativeHandle(ref, () => ({
|
|
51
|
+
show: (variant: AlertVariant, options: AlertOptions) => {
|
|
52
|
+
setQueue((prev) => [...prev, { ...options, variant }]);
|
|
53
|
+
},
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
React.useEffect(() => {
|
|
57
|
+
Alert.setRef({
|
|
58
|
+
show: (variant: AlertVariant, options: AlertOptions) => {
|
|
59
|
+
setQueue((prev) => [...prev, { ...options, variant }]);
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
if (!currentAlert) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const config = VARIANT_CONFIG[currentAlert.variant];
|
|
69
|
+
const IconComponent = config.icon;
|
|
70
|
+
const showIcon = !currentAlert.hideIcon;
|
|
71
|
+
const actions = currentAlert.actions?.length
|
|
72
|
+
? currentAlert.actions
|
|
73
|
+
: [{ label: 'Ok', variant: 'default' as const }];
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Modal isVisible={isVisible} onBackButtonPress={handleDismiss} onBackdropPress={handleDismiss}>
|
|
77
|
+
<View pointerEvents="box-none" style={tw`flex-1 items-center justify-center`}>
|
|
78
|
+
<View
|
|
79
|
+
style={tw`dark:border-divider dark:bg-sheet mx-8 w-[85%] rounded-2xl border border-gray-200 bg-white p-6`}
|
|
80
|
+
>
|
|
81
|
+
{/* Icon */}
|
|
82
|
+
{showIcon && (
|
|
83
|
+
<View
|
|
84
|
+
style={tw`dark:bg-surface mb-4 h-10 w-10 items-center justify-center rounded-full bg-gray-100`}
|
|
85
|
+
>
|
|
86
|
+
<IconComponent color={config.color} height={ICON_SIZE} width={ICON_SIZE} />
|
|
87
|
+
</View>
|
|
88
|
+
)}
|
|
89
|
+
|
|
90
|
+
{/* Title */}
|
|
91
|
+
<Typography style={tw`dark:text-foreground text-lg font-semibold text-gray-900`}>
|
|
92
|
+
{currentAlert.title}
|
|
93
|
+
</Typography>
|
|
94
|
+
|
|
95
|
+
{/* Message */}
|
|
96
|
+
{currentAlert.message ? (
|
|
97
|
+
<Typography style={tw`dark:text-subtitle mt-4 text-sm leading-relaxed text-gray-500`}>
|
|
98
|
+
{currentAlert.message}
|
|
99
|
+
</Typography>
|
|
100
|
+
) : null}
|
|
101
|
+
|
|
102
|
+
{/* Action buttons */}
|
|
103
|
+
<View style={tw`mt-6 gap-3`}>
|
|
104
|
+
{actions.map((action) =>
|
|
105
|
+
action.variant === 'cancel' ? (
|
|
106
|
+
<Pressable
|
|
107
|
+
key={action.label}
|
|
108
|
+
style={tw`dark:border-divider dark:bg-surface items-center rounded-xl border border-gray-200 bg-gray-50 py-3.5`}
|
|
109
|
+
onPress={() => handleActionPress(action.onPress)}
|
|
110
|
+
>
|
|
111
|
+
<Typography
|
|
112
|
+
style={tw`dark:text-foreground text-base font-semibold text-gray-700`}
|
|
113
|
+
>
|
|
114
|
+
{action.label}
|
|
115
|
+
</Typography>
|
|
116
|
+
</Pressable>
|
|
117
|
+
) : (
|
|
118
|
+
<Pressable
|
|
119
|
+
key={action.label}
|
|
120
|
+
style={tw`bg-primary-500 items-center rounded-xl py-3.5`}
|
|
121
|
+
onPress={() => handleActionPress(action.onPress)}
|
|
122
|
+
>
|
|
123
|
+
<Typography style={tw`text-base font-semibold text-white`}>
|
|
124
|
+
{action.label}
|
|
125
|
+
</Typography>
|
|
126
|
+
</Pressable>
|
|
127
|
+
),
|
|
128
|
+
)}
|
|
129
|
+
</View>
|
|
130
|
+
</View>
|
|
131
|
+
</View>
|
|
132
|
+
</Modal>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type AlertVariant = 'error' | 'info' | 'success' | 'warning';
|
|
2
|
+
|
|
3
|
+
export type AlertAction = {
|
|
4
|
+
label: string;
|
|
5
|
+
onPress?: () => void;
|
|
6
|
+
variant?: 'cancel' | 'default';
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type AlertOptions = {
|
|
10
|
+
actions?: AlertAction[];
|
|
11
|
+
hideIcon?: boolean;
|
|
12
|
+
message?: string;
|
|
13
|
+
title: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type AlertManagerRef = {
|
|
17
|
+
show: (variant: AlertVariant, options: AlertOptions) => void;
|
|
18
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AlertManagerRef, AlertOptions } from './alert-manager.types';
|
|
2
|
+
|
|
3
|
+
class AlertService {
|
|
4
|
+
private static ref: AlertManagerRef | null = null;
|
|
5
|
+
|
|
6
|
+
static error(options: AlertOptions) {
|
|
7
|
+
AlertService.ref?.show('error', options);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static info(options: AlertOptions) {
|
|
11
|
+
AlertService.ref?.show('info', options);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static setRef(ref: AlertManagerRef) {
|
|
15
|
+
AlertService.ref = ref;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static success(options: AlertOptions) {
|
|
19
|
+
AlertService.ref?.show('success', options);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static warning(options: AlertOptions) {
|
|
23
|
+
AlertService.ref?.show('warning', options);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { AlertService as Alert };
|
package/templates/shared/apps/mobile/src/components/atoms/BottomSheet/bottom-sheet.component.tsx
CHANGED
|
@@ -24,6 +24,7 @@ export type BottomSheetProps = DefaultComponentProps & {
|
|
|
24
24
|
enableDynamicSizing?: boolean;
|
|
25
25
|
enablePanDownToClose?: boolean;
|
|
26
26
|
handleComponent?: FC<BottomSheetHandleProps> | null;
|
|
27
|
+
hasScrollView?: boolean;
|
|
27
28
|
onExpand?: () => void;
|
|
28
29
|
sheetRef: RefObject<BottomSheetModal>;
|
|
29
30
|
snapPoints?: string[];
|
|
@@ -83,6 +84,7 @@ export function BottomSheet(props: BottomSheetProps) {
|
|
|
83
84
|
enableDynamicSizing = false,
|
|
84
85
|
enablePanDownToClose = true,
|
|
85
86
|
handleComponent = BottomSheetHandle,
|
|
87
|
+
hasScrollView = true,
|
|
86
88
|
sheetRef,
|
|
87
89
|
snapPoints = DEFAULT_SNAP_POINTS,
|
|
88
90
|
style,
|
|
@@ -104,11 +106,11 @@ export function BottomSheet(props: BottomSheetProps) {
|
|
|
104
106
|
ref={sheetRef}
|
|
105
107
|
android_keyboardInputMode="adjustResize"
|
|
106
108
|
backdropComponent={renderBackdrop}
|
|
107
|
-
backgroundStyle={[tw`bg-
|
|
109
|
+
backgroundStyle={[tw`dark:bg-sheet bg-white`, backgroundStyle]}
|
|
108
110
|
enableDynamicSizing={enableDynamicSizing}
|
|
109
111
|
enablePanDownToClose={enablePanDownToClose}
|
|
110
112
|
handleComponent={handleComponent}
|
|
111
|
-
handleIndicatorStyle={tw`bg-gray-
|
|
113
|
+
handleIndicatorStyle={tw`dark:bg-divider bg-gray-300`}
|
|
112
114
|
handleStyle={tw`rounded-tl-xl rounded-tr-xl`}
|
|
113
115
|
keyboardBehavior="interactive"
|
|
114
116
|
snapPoints={points}
|
|
@@ -120,12 +122,16 @@ export function BottomSheet(props: BottomSheetProps) {
|
|
|
120
122
|
style,
|
|
121
123
|
]}
|
|
122
124
|
>
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
{hasScrollView ? (
|
|
126
|
+
<BottomSheetScrollView
|
|
127
|
+
contentContainerStyle={contentContainerStyle}
|
|
128
|
+
keyboardShouldPersistTaps="handled"
|
|
129
|
+
>
|
|
130
|
+
{children}
|
|
131
|
+
</BottomSheetScrollView>
|
|
132
|
+
) : (
|
|
133
|
+
children
|
|
134
|
+
)}
|
|
129
135
|
</BottomSheetModal>
|
|
130
136
|
);
|
|
131
137
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import React, { ReactNode, useState } from 'react';
|
|
3
|
+
import { StyleProp, TouchableOpacity, ViewStyle } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import { DatePicker } from '@/components/atoms/DatePicker';
|
|
6
|
+
import { Typography } from '@/components/atoms/Typography';
|
|
7
|
+
import { defaultInputContainerStyle, defaultInputTextStyle, tw } from '@/tailwind';
|
|
8
|
+
|
|
9
|
+
const DEFAULT_FORMAT = 'MMMM D, YYYY';
|
|
10
|
+
|
|
11
|
+
type Props = {
|
|
12
|
+
error?: string;
|
|
13
|
+
format?: string;
|
|
14
|
+
maximumDate?: Date;
|
|
15
|
+
minimumDate?: Date;
|
|
16
|
+
onChange: (date: Date) => void;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
renderRight?: (value: Date) => ReactNode;
|
|
19
|
+
value: Date | undefined;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function DateModalInput(props: Props) {
|
|
23
|
+
const {
|
|
24
|
+
error,
|
|
25
|
+
format = DEFAULT_FORMAT,
|
|
26
|
+
maximumDate,
|
|
27
|
+
minimumDate,
|
|
28
|
+
onChange,
|
|
29
|
+
placeholder = 'Select a date',
|
|
30
|
+
renderRight,
|
|
31
|
+
value,
|
|
32
|
+
} = props;
|
|
33
|
+
const [showDatePicker, setShowDatePicker] = useState(false);
|
|
34
|
+
|
|
35
|
+
const containerStyle: StyleProp<ViewStyle> = [
|
|
36
|
+
defaultInputContainerStyle,
|
|
37
|
+
tw`dark:border-divider dark:bg-surface items-center`,
|
|
38
|
+
error && tw`border-red-500`,
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<>
|
|
43
|
+
<TouchableOpacity
|
|
44
|
+
activeOpacity={0.7}
|
|
45
|
+
style={containerStyle}
|
|
46
|
+
onPress={() => setShowDatePicker(true)}
|
|
47
|
+
>
|
|
48
|
+
<Typography
|
|
49
|
+
style={[defaultInputTextStyle, tw`dark:text-foreground`, !value && tw`text-placeholder`]}
|
|
50
|
+
>
|
|
51
|
+
{value ? dayjs(value).format(format) : placeholder}
|
|
52
|
+
</Typography>
|
|
53
|
+
{value && renderRight?.(value)}
|
|
54
|
+
</TouchableOpacity>
|
|
55
|
+
<DatePicker
|
|
56
|
+
date={value || new Date()}
|
|
57
|
+
isVisible={showDatePicker}
|
|
58
|
+
maximumDate={maximumDate}
|
|
59
|
+
minimumDate={minimumDate}
|
|
60
|
+
mode="date"
|
|
61
|
+
onCancel={() => setShowDatePicker(false)}
|
|
62
|
+
onConfirm={(selectedDate) => {
|
|
63
|
+
setShowDatePicker(false);
|
|
64
|
+
onChange(selectedDate);
|
|
65
|
+
}}
|
|
66
|
+
/>
|
|
67
|
+
</>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './date-modal-input.component';
|
package/templates/shared/apps/mobile/src/components/atoms/DatePicker/date-picker.component.tsx
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TouchableHighlight } from 'react-native';
|
|
3
|
+
import DateTimePickerModal, {
|
|
4
|
+
ReactNativeModalDateTimePickerProps,
|
|
5
|
+
} from 'react-native-modal-datetime-picker';
|
|
6
|
+
|
|
7
|
+
import { Typography } from '@/components/atoms/Typography';
|
|
8
|
+
import { useLocalStorageStore } from '@/stores';
|
|
9
|
+
import { colors, tw } from '@/tailwind';
|
|
10
|
+
|
|
11
|
+
type Props = Omit<ReactNativeModalDateTimePickerProps, 'onCancel' | 'onConfirm'> & {
|
|
12
|
+
onCancel: () => void;
|
|
13
|
+
onConfirm: (date: Date) => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function DatePicker(props: Props) {
|
|
17
|
+
const { onCancel, onConfirm, ...rest } = props;
|
|
18
|
+
const colorScheme = useLocalStorageStore((s) => s.colorScheme);
|
|
19
|
+
const isDark = colorScheme === 'dark';
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<DateTimePickerModal
|
|
23
|
+
backdropStyleIOS={tw`bg-black/40`}
|
|
24
|
+
buttonTextColorIOS={colors.primary[500]}
|
|
25
|
+
customCancelButtonIOS={({ onPress }) => (
|
|
26
|
+
<TouchableHighlight
|
|
27
|
+
style={tw`dark:bg-surface mt-2 items-center rounded-2xl bg-white py-4`}
|
|
28
|
+
underlayColor={isDark ? colors.background : colors.gray[100]}
|
|
29
|
+
onPress={onPress}
|
|
30
|
+
>
|
|
31
|
+
<Typography style={tw`dark:text-subtitle text-base font-semibold text-gray-500`}>
|
|
32
|
+
Cancel
|
|
33
|
+
</Typography>
|
|
34
|
+
</TouchableHighlight>
|
|
35
|
+
)}
|
|
36
|
+
isDarkModeEnabled={isDark}
|
|
37
|
+
modalStyleIOS={tw`rounded-2xl pb-4`}
|
|
38
|
+
pickerContainerStyleIOS={tw`dark:bg-sheet items-center justify-center bg-white`}
|
|
39
|
+
onCancel={onCancel}
|
|
40
|
+
onConfirm={onConfirm}
|
|
41
|
+
{...rest}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './date-picker.component';
|