react-native-platform-components 0.5.0 → 0.5.1
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/README.md +36 -30
- package/android/src/main/java/com/platformcomponents/PCSelectionMenuView.kt +28 -2
- package/ios/PCSelectionMenu.swift +44 -8
- package/lib/module/DatePicker.js +11 -11
- package/lib/module/DatePicker.js.map +1 -1
- package/lib/module/DatePickerNativeComponent.ts +5 -4
- package/lib/typescript/src/DatePicker.d.ts.map +1 -1
- package/lib/typescript/src/DatePickerNativeComponent.d.ts +5 -4
- package/lib/typescript/src/DatePickerNativeComponent.d.ts.map +1 -1
- package/package.json +1 -1
- package/shared/PCSelectionMenuShadowNode-custom.cpp +1 -1
- package/src/DatePicker.tsx +16 -16
- package/src/DatePickerNativeComponent.ts +5 -4
package/README.md
CHANGED
|
@@ -5,39 +5,45 @@
|
|
|
5
5
|
|
|
6
6
|
<table>
|
|
7
7
|
<tr>
|
|
8
|
-
<td
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
8
|
+
<td valign="top">
|
|
9
|
+
<table>
|
|
10
|
+
<tr>
|
|
11
|
+
<td align="center"><strong>iOS DatePicker</strong></td>
|
|
12
|
+
<td align="center"><strong>Android DatePicker</strong></td>
|
|
13
|
+
</tr>
|
|
14
|
+
<tr>
|
|
15
|
+
<td><img src="https://raw.githubusercontent.com/JarX-Concepts/react-native-platform-components/main/assets/ios-datepicker.gif" height="350" /></td>
|
|
16
|
+
<td><img src="https://raw.githubusercontent.com/JarX-Concepts/react-native-platform-components/main/assets/android-datepicker.gif" height="350" /></td>
|
|
17
|
+
</tr>
|
|
18
|
+
<tr>
|
|
19
|
+
<td align="center"><strong>iOS SelectionMenu</strong></td>
|
|
20
|
+
<td align="center"><strong>Android SelectionMenu</strong></td>
|
|
21
|
+
</tr>
|
|
22
|
+
<tr>
|
|
23
|
+
<td><img src="https://raw.githubusercontent.com/JarX-Concepts/react-native-platform-components/main/assets/ios-selectionmenu.gif" height="350" /></td>
|
|
24
|
+
<td><img src="https://raw.githubusercontent.com/JarX-Concepts/react-native-platform-components/main/assets/android-selectionmenu.gif" height="350" /></td>
|
|
25
|
+
</tr>
|
|
26
|
+
</table>
|
|
27
|
+
</td>
|
|
28
|
+
<td valign="top">
|
|
29
|
+
<blockquote>🚧 In development — not ready for public use.</blockquote>
|
|
30
|
+
<p>High-quality <strong>native UI components for React Native</strong>, implemented with platform-first APIs and exposed through clean, typed JavaScript interfaces.</p>
|
|
31
|
+
<p>This library focuses on <strong>true native behavior</strong>, not JavaScript re-implementations — providing:</p>
|
|
32
|
+
<ul>
|
|
33
|
+
<li><strong>SelectionMenu</strong> – native selection menus (Material on Android, system menus on iOS)</li>
|
|
34
|
+
<li><strong>DatePicker</strong> – native date & time pickers with modal and embedded presentations</li>
|
|
35
|
+
</ul>
|
|
36
|
+
<p>The goal is to provide components that:</p>
|
|
37
|
+
<ul>
|
|
38
|
+
<li>Feel <strong>100% native</strong> on each platform</li>
|
|
39
|
+
<li>Support modern platform design systems (Material 3 on Android, system pickers on iOS)</li>
|
|
40
|
+
<li>Offer <strong>headless</strong> and <strong>inline</strong> modes for maximum layout control</li>
|
|
41
|
+
<li>Integrate cleanly with <strong>React Native Codegen / Fabric</strong></li>
|
|
42
|
+
</ul>
|
|
43
|
+
</td>
|
|
22
44
|
</tr>
|
|
23
45
|
</table>
|
|
24
46
|
|
|
25
|
-
> 🚧 In development — not ready for public use.
|
|
26
|
-
|
|
27
|
-
High-quality **native UI components for React Native**, implemented with platform-first APIs and exposed through clean, typed JavaScript interfaces.
|
|
28
|
-
|
|
29
|
-
This library focuses on **true native behavior**, not JavaScript re-implementations — providing:
|
|
30
|
-
|
|
31
|
-
- **SelectionMenu** – native selection menus (Material on Android, system menus on iOS)
|
|
32
|
-
- **DatePicker** – native date & time pickers with modal and embedded presentations
|
|
33
|
-
|
|
34
|
-
The goal is to provide components that:
|
|
35
|
-
|
|
36
|
-
- Feel **100% native** on each platform
|
|
37
|
-
- Support modern platform design systems (Material 3 on Android, system pickers on iOS)
|
|
38
|
-
- Offer **headless** and **inline** modes for maximum layout control
|
|
39
|
-
- Integrate cleanly with **React Native Codegen / Fabric**
|
|
40
|
-
|
|
41
47
|
---
|
|
42
48
|
|
|
43
49
|
## Installation
|
|
@@ -51,6 +51,7 @@ class PCSelectionMenuView(context: Context) : FrameLayout(context) {
|
|
|
51
51
|
private var headlessMenuShowing = false
|
|
52
52
|
private var headlessDismissProgrammatic = false
|
|
53
53
|
private var headlessDismissAfterSelect = false
|
|
54
|
+
private var headlessOpenToken = 0
|
|
54
55
|
|
|
55
56
|
init {
|
|
56
57
|
minimumHeight = 0
|
|
@@ -129,6 +130,13 @@ class PCSelectionMenuView(context: Context) : FrameLayout(context) {
|
|
|
129
130
|
if (anchorMode == newMode) return
|
|
130
131
|
anchorMode = newMode
|
|
131
132
|
Log.d(TAG, "applyAnchorMode anchorMode=$anchorMode")
|
|
133
|
+
if (anchorMode != "headless") {
|
|
134
|
+
headlessOpenToken += 1
|
|
135
|
+
if (headlessMenuShowing) {
|
|
136
|
+
headlessDismissProgrammatic = true
|
|
137
|
+
headlessMenu?.dismiss()
|
|
138
|
+
}
|
|
139
|
+
}
|
|
132
140
|
rebuildUI()
|
|
133
141
|
}
|
|
134
142
|
|
|
@@ -138,11 +146,13 @@ class PCSelectionMenuView(context: Context) : FrameLayout(context) {
|
|
|
138
146
|
else -> "closed"
|
|
139
147
|
}
|
|
140
148
|
Log.d(TAG, "applyVisible visible=$visible anchorMode=$anchorMode")
|
|
149
|
+
headlessOpenToken += 1
|
|
150
|
+
val token = headlessOpenToken
|
|
141
151
|
|
|
142
152
|
if (anchorMode != "headless") return
|
|
143
153
|
|
|
144
154
|
if (visible == "open") {
|
|
145
|
-
presentHeadlessIfNeeded()
|
|
155
|
+
presentHeadlessIfNeeded(token)
|
|
146
156
|
} else {
|
|
147
157
|
Log.d(TAG, "applyVisible close -> dismiss")
|
|
148
158
|
if (headlessMenuShowing) {
|
|
@@ -162,6 +172,10 @@ class PCSelectionMenuView(context: Context) : FrameLayout(context) {
|
|
|
162
172
|
// ---- UI building ----
|
|
163
173
|
|
|
164
174
|
private fun rebuildUI() {
|
|
175
|
+
if (headlessMenuShowing) {
|
|
176
|
+
headlessDismissProgrammatic = true
|
|
177
|
+
headlessMenu?.dismiss()
|
|
178
|
+
}
|
|
165
179
|
inlineText?.dismissDropDown()
|
|
166
180
|
detachInlineDropdownOverlay()
|
|
167
181
|
inlineDropdownOverlay = null
|
|
@@ -465,7 +479,7 @@ class PCSelectionMenuView(context: Context) : FrameLayout(context) {
|
|
|
465
479
|
|
|
466
480
|
// ---- Headless open ----
|
|
467
481
|
|
|
468
|
-
private fun presentHeadlessIfNeeded() {
|
|
482
|
+
private fun presentHeadlessIfNeeded(token: Int) {
|
|
469
483
|
val popup = headlessMenu ?: return
|
|
470
484
|
if (interactivity != "enabled") {
|
|
471
485
|
Log.d(TAG, "presentHeadlessIfNeeded interactivity=$interactivity -> requestClose")
|
|
@@ -473,6 +487,18 @@ class PCSelectionMenuView(context: Context) : FrameLayout(context) {
|
|
|
473
487
|
return
|
|
474
488
|
}
|
|
475
489
|
post {
|
|
490
|
+
if (token != headlessOpenToken) {
|
|
491
|
+
Log.d(TAG, "presentHeadlessIfNeeded stale token -> skip")
|
|
492
|
+
return@post
|
|
493
|
+
}
|
|
494
|
+
if (anchorMode != "headless" || visible != "open") {
|
|
495
|
+
Log.d(TAG, "presentHeadlessIfNeeded no longer open -> skip")
|
|
496
|
+
return@post
|
|
497
|
+
}
|
|
498
|
+
if (interactivity != "enabled") {
|
|
499
|
+
Log.d(TAG, "presentHeadlessIfNeeded disabled -> skip")
|
|
500
|
+
return@post
|
|
501
|
+
}
|
|
476
502
|
if (!isAttachedToWindow) {
|
|
477
503
|
Log.d(TAG, "presentHeadlessIfNeeded not attached -> requestClose")
|
|
478
504
|
onRequestClose?.invoke()
|
|
@@ -98,6 +98,8 @@ public final class PCSelectionMenuView: UIControl {
|
|
|
98
98
|
private let model = PCSelectionMenuModel()
|
|
99
99
|
private var hostingController: UIHostingController<AnyView>?
|
|
100
100
|
private var headlessMenuView: UIView?
|
|
101
|
+
private var headlessMenuVC: UIViewController?
|
|
102
|
+
private var headlessPresentationToken: Int = 0
|
|
101
103
|
|
|
102
104
|
private var parsedOptions: [PCSelectionMenuOption] {
|
|
103
105
|
options.compactMap { any in
|
|
@@ -132,12 +134,16 @@ public final class PCSelectionMenuView: UIControl {
|
|
|
132
134
|
alpha = disabled ? 0.5 : 1.0
|
|
133
135
|
isUserInteractionEnabled = !disabled
|
|
134
136
|
accessibilityTraits = disabled ? [.notEnabled] : [.button]
|
|
137
|
+
if disabled {
|
|
138
|
+
dismissHeadlessIfNeeded()
|
|
139
|
+
}
|
|
135
140
|
}
|
|
136
141
|
|
|
137
142
|
// MARK: - Inline vs headless
|
|
138
143
|
|
|
139
144
|
private func updateAnchorMode() {
|
|
140
145
|
if anchorMode == "inline" {
|
|
146
|
+
dismissHeadlessIfNeeded()
|
|
141
147
|
uninstallHeadlessIfNeeded()
|
|
142
148
|
installInlineIfNeeded()
|
|
143
149
|
sync()
|
|
@@ -248,15 +254,22 @@ public final class PCSelectionMenuView: UIControl {
|
|
|
248
254
|
|
|
249
255
|
private func updatePresentation() {
|
|
250
256
|
guard anchorMode != "inline" else { return }
|
|
251
|
-
|
|
257
|
+
headlessPresentationToken += 1
|
|
252
258
|
|
|
253
|
-
if visible == "open" {
|
|
254
|
-
presentHeadlessMenuIfNeeded()
|
|
259
|
+
if visible == "open" && interactivity != "disabled" {
|
|
260
|
+
presentHeadlessMenuIfNeeded(token: headlessPresentationToken)
|
|
261
|
+
} else {
|
|
262
|
+
dismissHeadlessIfNeeded()
|
|
255
263
|
}
|
|
256
|
-
// Note: dismissal is handled by the menu itself calling onRequestClose
|
|
257
264
|
}
|
|
258
265
|
|
|
259
|
-
private func
|
|
266
|
+
private func dismissHeadlessIfNeeded() {
|
|
267
|
+
guard let vc = headlessMenuVC else { return }
|
|
268
|
+
headlessMenuVC = nil
|
|
269
|
+
vc.dismiss(animated: true)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private func presentHeadlessMenuIfNeeded(token: Int) {
|
|
260
273
|
guard headlessMenuView != nil else { return }
|
|
261
274
|
guard let vc = nearestViewController() else { return }
|
|
262
275
|
|
|
@@ -264,7 +277,15 @@ public final class PCSelectionMenuView: UIControl {
|
|
|
264
277
|
guard !opts.isEmpty else { return }
|
|
265
278
|
|
|
266
279
|
logger.debug("presentHeadlessMenuIfNeeded: scheduling presentation with \(opts.count) options")
|
|
267
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + PCConstants.headlessPresentationDelay) {
|
|
280
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + PCConstants.headlessPresentationDelay) { [weak self] in
|
|
281
|
+
guard let self else { return }
|
|
282
|
+
guard self.headlessPresentationToken == token else { return }
|
|
283
|
+
guard self.visible == "open" else { return }
|
|
284
|
+
guard self.anchorMode != "inline" else { return }
|
|
285
|
+
guard self.interactivity != "disabled" else { return }
|
|
286
|
+
guard self.headlessMenuVC == nil else { return }
|
|
287
|
+
guard self.window != nil else { return }
|
|
288
|
+
|
|
268
289
|
let menuVC = PCMenuViewController(
|
|
269
290
|
options: opts,
|
|
270
291
|
onSelect: { [weak self] idx in
|
|
@@ -277,6 +298,9 @@ public final class PCSelectionMenuView: UIControl {
|
|
|
277
298
|
onCancel: { [weak self] in
|
|
278
299
|
logger.debug("headless menu cancelled")
|
|
279
300
|
self?.onRequestClose?()
|
|
301
|
+
},
|
|
302
|
+
onDismiss: { [weak self] in
|
|
303
|
+
self?.headlessMenuVC = nil
|
|
280
304
|
}
|
|
281
305
|
)
|
|
282
306
|
|
|
@@ -317,6 +341,7 @@ public final class PCSelectionMenuView: UIControl {
|
|
|
317
341
|
menuVC.modalTransitionStyle = .crossDissolve
|
|
318
342
|
menuVC.menuFrame = menuFrame
|
|
319
343
|
|
|
344
|
+
self.headlessMenuVC = menuVC
|
|
320
345
|
vc.present(menuVC, animated: true)
|
|
321
346
|
}
|
|
322
347
|
}
|
|
@@ -377,15 +402,22 @@ private class PCMenuViewController: UIViewController, UITableViewDelegate, UITab
|
|
|
377
402
|
private let options: [PCSelectionMenuOption]
|
|
378
403
|
private let onSelect: (Int) -> Void
|
|
379
404
|
private let onCancel: () -> Void
|
|
405
|
+
private let onDismiss: () -> Void
|
|
380
406
|
private var tableView: UITableView!
|
|
381
407
|
private var menuContainer: UIView!
|
|
382
408
|
|
|
383
409
|
var menuFrame: CGRect = .zero
|
|
384
410
|
|
|
385
|
-
init(
|
|
411
|
+
init(
|
|
412
|
+
options: [PCSelectionMenuOption],
|
|
413
|
+
onSelect: @escaping (Int) -> Void,
|
|
414
|
+
onCancel: @escaping () -> Void,
|
|
415
|
+
onDismiss: @escaping () -> Void
|
|
416
|
+
) {
|
|
386
417
|
self.options = options
|
|
387
418
|
self.onSelect = onSelect
|
|
388
419
|
self.onCancel = onCancel
|
|
420
|
+
self.onDismiss = onDismiss
|
|
389
421
|
super.init(nibName: nil, bundle: nil)
|
|
390
422
|
}
|
|
391
423
|
|
|
@@ -439,6 +471,11 @@ private class PCMenuViewController: UIViewController, UITableViewDelegate, UITab
|
|
|
439
471
|
effectView.contentView.addSubview(tableView)
|
|
440
472
|
}
|
|
441
473
|
|
|
474
|
+
override func viewDidDisappear(_ animated: Bool) {
|
|
475
|
+
super.viewDidDisappear(animated)
|
|
476
|
+
onDismiss()
|
|
477
|
+
}
|
|
478
|
+
|
|
442
479
|
@objc private func handleBackgroundTap(_ gesture: UITapGestureRecognizer) {
|
|
443
480
|
let location = gesture.location(in: view)
|
|
444
481
|
if !menuContainer.frame.contains(location) {
|
|
@@ -465,4 +502,3 @@ private class PCMenuViewController: UIViewController, UITableViewDelegate, UITab
|
|
|
465
502
|
}
|
|
466
503
|
}
|
|
467
504
|
}
|
|
468
|
-
|
package/lib/module/DatePicker.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
// DatePicker.tsx
|
|
4
|
-
import React, { useCallback
|
|
4
|
+
import React, { useCallback } from 'react';
|
|
5
5
|
import { StyleSheet } from 'react-native';
|
|
6
6
|
import NativeDatePicker from './DatePickerNativeComponent';
|
|
7
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
@@ -9,7 +9,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
9
9
|
// conflict with valid negative timestamps (dates before 1970).
|
|
10
10
|
const NO_DATE_SENTINEL = Number.MIN_SAFE_INTEGER;
|
|
11
11
|
function dateToMsOrSentinel(d) {
|
|
12
|
-
|
|
12
|
+
if (!d) return NO_DATE_SENTINEL;
|
|
13
|
+
const ms = d.getTime();
|
|
14
|
+
return Number.isFinite(ms) ? ms : NO_DATE_SENTINEL;
|
|
13
15
|
}
|
|
14
16
|
function normalizeVisible(presentation, visible) {
|
|
15
17
|
// Only meaningful in modal presentation. Keep undefined for inline to avoid noise.
|
|
@@ -33,13 +35,13 @@ export function DatePicker(props) {
|
|
|
33
35
|
android,
|
|
34
36
|
testID
|
|
35
37
|
} = props;
|
|
38
|
+
const isModal = presentation === 'modal';
|
|
36
39
|
const handleConfirm = useCallback(e => {
|
|
37
40
|
onConfirm?.(new Date(e.nativeEvent.timestampMs));
|
|
38
41
|
}, [onConfirm]);
|
|
39
42
|
const handleClosed = useCallback(() => {
|
|
40
43
|
onClosed?.();
|
|
41
44
|
}, [onClosed]);
|
|
42
|
-
const styles = useMemo(() => createStyles(), []);
|
|
43
45
|
const nativeProps = {
|
|
44
46
|
style: [styles.picker, style],
|
|
45
47
|
mode,
|
|
@@ -48,10 +50,10 @@ export function DatePicker(props) {
|
|
|
48
50
|
presentation,
|
|
49
51
|
visible: normalizeVisible(presentation, visible),
|
|
50
52
|
dateMs: dateToMsOrSentinel(date),
|
|
51
|
-
minDateMs: dateToMsOrSentinel(minDate
|
|
52
|
-
maxDateMs: dateToMsOrSentinel(maxDate
|
|
53
|
+
minDateMs: dateToMsOrSentinel(minDate),
|
|
54
|
+
maxDateMs: dateToMsOrSentinel(maxDate),
|
|
53
55
|
onConfirm: onConfirm ? handleConfirm : undefined,
|
|
54
|
-
onClosed: onClosed ? handleClosed : undefined,
|
|
56
|
+
onClosed: isModal && onClosed ? handleClosed : undefined,
|
|
55
57
|
ios: ios ? {
|
|
56
58
|
preferredStyle: ios.preferredStyle,
|
|
57
59
|
countDownDurationSeconds: ios.countDownDurationSeconds,
|
|
@@ -71,9 +73,7 @@ export function DatePicker(props) {
|
|
|
71
73
|
...nativeProps
|
|
72
74
|
});
|
|
73
75
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
});
|
|
78
|
-
}
|
|
76
|
+
const styles = StyleSheet.create({
|
|
77
|
+
picker: {}
|
|
78
|
+
});
|
|
79
79
|
//# sourceMappingURL=DatePicker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","useCallback","
|
|
1
|
+
{"version":3,"names":["React","useCallback","StyleSheet","NativeDatePicker","jsx","_jsx","NO_DATE_SENTINEL","Number","MIN_SAFE_INTEGER","dateToMsOrSentinel","d","ms","getTime","isFinite","normalizeVisible","presentation","visible","undefined","DatePicker","props","style","date","minDate","maxDate","locale","timeZoneName","mode","onConfirm","onClosed","ios","android","testID","isModal","handleConfirm","e","Date","nativeEvent","timestampMs","handleClosed","nativeProps","styles","picker","dateMs","minDateMs","maxDateMs","preferredStyle","countDownDurationSeconds","minuteInterval","roundsToMinuteInterval","firstDayOfWeek","material","dialogTitle","positiveButtonTitle","negativeButtonTitle","create"],"sourceRoot":"../../src","sources":["DatePicker.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -35,14 +35,15 @@ export type MacOSProps = Readonly<{}>;
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Sentinel convention:
|
|
38
|
-
* -
|
|
38
|
+
* - `Number.MIN_SAFE_INTEGER` means "no value / unbounded / unset".
|
|
39
|
+
* (Allows negative timestamps for pre-1970 dates.)
|
|
39
40
|
*/
|
|
40
41
|
export type CommonProps = {
|
|
41
42
|
mode?: string; // DatePickerMode
|
|
42
43
|
|
|
43
|
-
dateMs?: CodegenTypes.WithDefault<TimestampMs, -
|
|
44
|
-
minDateMs?: CodegenTypes.WithDefault<TimestampMs, -
|
|
45
|
-
maxDateMs?: CodegenTypes.WithDefault<TimestampMs, -
|
|
44
|
+
dateMs?: CodegenTypes.WithDefault<TimestampMs, -9007199254740991>;
|
|
45
|
+
minDateMs?: CodegenTypes.WithDefault<TimestampMs, -9007199254740991>;
|
|
46
|
+
maxDateMs?: CodegenTypes.WithDefault<TimestampMs, -9007199254740991>;
|
|
46
47
|
|
|
47
48
|
locale?: string;
|
|
48
49
|
timeZoneName?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../../src/DatePicker.tsx"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../../src/DatePicker.tsx"],"names":[],"mappings":"AACA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAC3C,OAAO,KAAK,EAAwB,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG/E,OAAyB,EAGvB,KAAK,QAAQ,IAAI,cAAc,EAC/B,KAAK,YAAY,IAAI,kBAAkB,EACvC,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,KAAK,yBAAyB,EAC9B,KAAK,kBAAkB,EACxB,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,mBAAmB,EAAW,MAAM,eAAe,CAAC;AAElE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAE7B,2DAA2D;IAC3D,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAElB,mDAAmD;IACnD,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAEtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,YAAY,CAAC,EAAE,sBAAsB,CAAC;IAEtC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAEtB,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,GAAG,CAAC,EAAE;QACJ,cAAc,CAAC,EAAE,kBAAkB,CAAC;QACpC,wBAAwB,CAAC,EAAE,cAAc,CAAC,0BAA0B,CAAC,CAAC;QACtE,cAAc,CAAC,EAAE,cAAc,CAAC,gBAAgB,CAAC,CAAC;QAClD,sBAAsB,CAAC,EAAE,yBAAyB,CAAC;KACpD,CAAC;IAEF,OAAO,CAAC,EAAE;QACR,cAAc,CAAC,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;QACtD,QAAQ,CAAC,EAAE,mBAAmB,CAAC;QAC/B,WAAW,CAAC,EAAE,kBAAkB,CAAC,aAAa,CAAC,CAAC;QAChD,mBAAmB,CAAC,EAAE,kBAAkB,CAAC,qBAAqB,CAAC,CAAC;QAChE,mBAAmB,CAAC,EAAE,kBAAkB,CAAC,qBAAqB,CAAC,CAAC;KACjE,CAAC;CACH,CAAC;AAqBF,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,KAAK,CAAC,YAAY,CAqErE"}
|
|
@@ -25,13 +25,14 @@ export type WindowsProps = Readonly<{}>;
|
|
|
25
25
|
export type MacOSProps = Readonly<{}>;
|
|
26
26
|
/**
|
|
27
27
|
* Sentinel convention:
|
|
28
|
-
* -
|
|
28
|
+
* - `Number.MIN_SAFE_INTEGER` means "no value / unbounded / unset".
|
|
29
|
+
* (Allows negative timestamps for pre-1970 dates.)
|
|
29
30
|
*/
|
|
30
31
|
export type CommonProps = {
|
|
31
32
|
mode?: string;
|
|
32
|
-
dateMs?: CodegenTypes.WithDefault<TimestampMs, -
|
|
33
|
-
minDateMs?: CodegenTypes.WithDefault<TimestampMs, -
|
|
34
|
-
maxDateMs?: CodegenTypes.WithDefault<TimestampMs, -
|
|
33
|
+
dateMs?: CodegenTypes.WithDefault<TimestampMs, -9007199254740991>;
|
|
34
|
+
minDateMs?: CodegenTypes.WithDefault<TimestampMs, -9007199254740991>;
|
|
35
|
+
maxDateMs?: CodegenTypes.WithDefault<TimestampMs, -9007199254740991>;
|
|
35
36
|
locale?: string;
|
|
36
37
|
timeZoneName?: string;
|
|
37
38
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePickerNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/DatePickerNativeComponent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG5D,MAAM,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,YAAY,CAAC,MAAM,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,gBAAgB,CAAC;AAChF,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,UAAU,CAAC;AAE1D,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAC/E,MAAM,MAAM,yBAAyB,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAExE,MAAM,MAAM,QAAQ,GAAG;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wBAAwB,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC;IAC/C,cAAc,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC;IACpC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,cAAc,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;AACpC,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;AACxC,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;AAEtC
|
|
1
|
+
{"version":3,"file":"DatePickerNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/DatePickerNativeComponent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG5D,MAAM,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,YAAY,CAAC,MAAM,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,gBAAgB,CAAC;AAChF,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,UAAU,CAAC;AAE1D,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAC/E,MAAM,MAAM,yBAAyB,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAExE,MAAM,MAAM,QAAQ,GAAG;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wBAAwB,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC;IAC/C,cAAc,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC;IACpC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,cAAc,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;AACpC,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;AACxC,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;AAEtC;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,MAAM,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,gBAAgB,CAAC,CAAC;IAClE,SAAS,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,gBAAgB,CAAC,CAAC;IACrE,SAAS,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,gBAAgB,CAAC,CAAC;IAErE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,WAAW,WAAY,SAAQ,SAAS,EAAE,WAAW;IACzD,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,SAAS,CAAC,EAAE,YAAY,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC;IAC/D,QAAQ,CAAC,EAAE,YAAY,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;CAC5D;;AAED,wBAAmE"}
|
package/package.json
CHANGED
package/src/DatePicker.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// DatePicker.tsx
|
|
2
|
-
import React, { useCallback
|
|
2
|
+
import React, { useCallback } from 'react';
|
|
3
3
|
import type { NativeSyntheticEvent, StyleProp, ViewStyle } from 'react-native';
|
|
4
4
|
import { StyleSheet } from 'react-native';
|
|
5
5
|
|
|
@@ -64,7 +64,9 @@ export type DatePickerProps = {
|
|
|
64
64
|
const NO_DATE_SENTINEL = Number.MIN_SAFE_INTEGER;
|
|
65
65
|
|
|
66
66
|
function dateToMsOrSentinel(d: Date | null | undefined): number {
|
|
67
|
-
|
|
67
|
+
if (!d) return NO_DATE_SENTINEL;
|
|
68
|
+
const ms = d.getTime();
|
|
69
|
+
return Number.isFinite(ms) ? ms : NO_DATE_SENTINEL;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
function normalizeVisible(
|
|
@@ -94,6 +96,8 @@ export function DatePicker(props: DatePickerProps): React.ReactElement {
|
|
|
94
96
|
testID,
|
|
95
97
|
} = props;
|
|
96
98
|
|
|
99
|
+
const isModal = presentation === 'modal';
|
|
100
|
+
|
|
97
101
|
const handleConfirm = useCallback(
|
|
98
102
|
(e: NativeSyntheticEvent<DateChangeEvent>) => {
|
|
99
103
|
onConfirm?.(new Date(e.nativeEvent.timestampMs));
|
|
@@ -105,24 +109,22 @@ export function DatePicker(props: DatePickerProps): React.ReactElement {
|
|
|
105
109
|
onClosed?.();
|
|
106
110
|
}, [onClosed]);
|
|
107
111
|
|
|
108
|
-
const styles = useMemo(() => createStyles(), []);
|
|
109
|
-
|
|
110
112
|
const nativeProps: NativeDatePickerProps = {
|
|
111
|
-
style: [styles.picker, style]
|
|
113
|
+
style: [styles.picker, style],
|
|
112
114
|
|
|
113
115
|
mode,
|
|
114
116
|
locale,
|
|
115
117
|
timeZoneName,
|
|
116
118
|
|
|
117
119
|
presentation,
|
|
118
|
-
visible: normalizeVisible(presentation, visible)
|
|
120
|
+
visible: normalizeVisible(presentation, visible),
|
|
119
121
|
|
|
120
|
-
dateMs: dateToMsOrSentinel(date)
|
|
121
|
-
minDateMs: dateToMsOrSentinel(minDate
|
|
122
|
-
maxDateMs: dateToMsOrSentinel(maxDate
|
|
122
|
+
dateMs: dateToMsOrSentinel(date),
|
|
123
|
+
minDateMs: dateToMsOrSentinel(minDate),
|
|
124
|
+
maxDateMs: dateToMsOrSentinel(maxDate),
|
|
123
125
|
|
|
124
126
|
onConfirm: onConfirm ? handleConfirm : undefined,
|
|
125
|
-
onClosed: onClosed ? handleClosed : undefined,
|
|
127
|
+
onClosed: isModal && onClosed ? handleClosed : undefined,
|
|
126
128
|
|
|
127
129
|
ios: ios
|
|
128
130
|
? {
|
|
@@ -136,7 +138,7 @@ export function DatePicker(props: DatePickerProps): React.ReactElement {
|
|
|
136
138
|
android: android
|
|
137
139
|
? {
|
|
138
140
|
firstDayOfWeek: android.firstDayOfWeek,
|
|
139
|
-
material: android.material
|
|
141
|
+
material: android.material,
|
|
140
142
|
dialogTitle: android.dialogTitle,
|
|
141
143
|
positiveButtonTitle: android.positiveButtonTitle,
|
|
142
144
|
negativeButtonTitle: android.negativeButtonTitle,
|
|
@@ -147,8 +149,6 @@ export function DatePicker(props: DatePickerProps): React.ReactElement {
|
|
|
147
149
|
return <NativeDatePicker testID={testID} {...nativeProps} />;
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
});
|
|
154
|
-
}
|
|
152
|
+
const styles = StyleSheet.create({
|
|
153
|
+
picker: {},
|
|
154
|
+
});
|
|
@@ -35,14 +35,15 @@ export type MacOSProps = Readonly<{}>;
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Sentinel convention:
|
|
38
|
-
* -
|
|
38
|
+
* - `Number.MIN_SAFE_INTEGER` means "no value / unbounded / unset".
|
|
39
|
+
* (Allows negative timestamps for pre-1970 dates.)
|
|
39
40
|
*/
|
|
40
41
|
export type CommonProps = {
|
|
41
42
|
mode?: string; // DatePickerMode
|
|
42
43
|
|
|
43
|
-
dateMs?: CodegenTypes.WithDefault<TimestampMs, -
|
|
44
|
-
minDateMs?: CodegenTypes.WithDefault<TimestampMs, -
|
|
45
|
-
maxDateMs?: CodegenTypes.WithDefault<TimestampMs, -
|
|
44
|
+
dateMs?: CodegenTypes.WithDefault<TimestampMs, -9007199254740991>;
|
|
45
|
+
minDateMs?: CodegenTypes.WithDefault<TimestampMs, -9007199254740991>;
|
|
46
|
+
maxDateMs?: CodegenTypes.WithDefault<TimestampMs, -9007199254740991>;
|
|
46
47
|
|
|
47
48
|
locale?: string;
|
|
48
49
|
timeZoneName?: string;
|