react-native-nitro-modal 0.2.0 → 0.3.0-rc
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 +48 -4
- package/android/src/main/java/com/margelo/nitro/nitromodal/BottomSheetPresenter.kt +335 -34
- package/android/src/main/java/com/margelo/nitro/nitromodal/HybridNitroModal.kt +63 -22
- package/android/src/main/java/com/margelo/nitro/nitromodal/ModalConfig.kt +31 -1
- package/android/src/main/java/com/margelo/nitro/nitromodal/ModalContentRoot.kt +48 -5
- package/android/src/main/java/com/margelo/nitro/nitromodal/ModalPresenter.kt +63 -4
- package/android/src/main/java/com/margelo/nitro/nitromodal/NitroModalHostView.kt +68 -8
- package/ios/HybridNitroModal.swift +111 -20
- package/ios/ModalConfig.swift +44 -2
- package/ios/NitroModalHostView.swift +73 -8
- package/ios/SheetView.swift +575 -0
- package/ios/SheetViewController.swift +19 -450
- package/lib/module/NitroModal.js +38 -5
- package/lib/module/NitroModal.js.map +1 -1
- package/lib/module/NitroModalView.web.js +55 -25
- package/lib/module/NitroModalView.web.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/web/AnimatedValue.js +3 -0
- package/lib/module/web/AnimatedValue.js.map +1 -1
- package/lib/module/web/ModalConfig.js +8 -4
- package/lib/module/web/ModalConfig.js.map +1 -1
- package/lib/module/web/ModalController.js +29 -2
- package/lib/module/web/ModalController.js.map +1 -1
- package/lib/module/web/ModalPresenter.js +47 -12
- package/lib/module/web/ModalPresenter.js.map +1 -1
- package/lib/module/web/SheetPresenter.js +54 -29
- package/lib/module/web/SheetPresenter.js.map +1 -1
- package/lib/module/web/dom.js +13 -0
- package/lib/module/web/dom.js.map +1 -1
- package/lib/typescript/src/NitroModal.d.ts +33 -2
- package/lib/typescript/src/NitroModal.d.ts.map +1 -1
- package/lib/typescript/src/NitroModal.nitro.d.ts +29 -2
- package/lib/typescript/src/NitroModal.nitro.d.ts.map +1 -1
- package/lib/typescript/src/NitroModalView.web.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/web/AnimatedValue.d.ts +1 -0
- package/lib/typescript/src/web/AnimatedValue.d.ts.map +1 -1
- package/lib/typescript/src/web/ModalConfig.d.ts +6 -0
- package/lib/typescript/src/web/ModalConfig.d.ts.map +1 -1
- package/lib/typescript/src/web/ModalController.d.ts +5 -0
- package/lib/typescript/src/web/ModalController.d.ts.map +1 -1
- package/lib/typescript/src/web/ModalPresenter.d.ts +9 -0
- package/lib/typescript/src/web/ModalPresenter.d.ts.map +1 -1
- package/lib/typescript/src/web/SheetPresenter.d.ts +1 -1
- package/lib/typescript/src/web/SheetPresenter.d.ts.map +1 -1
- package/lib/typescript/src/web/dom.d.ts +2 -0
- package/lib/typescript/src/web/dom.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JHybridNitroModalSpec.cpp +60 -6
- package/nitrogen/generated/android/c++/JHybridNitroModalSpec.hpp +10 -2
- package/nitrogen/generated/android/c++/JNamedSheetDetent.hpp +64 -0
- package/nitrogen/generated/android/c++/JSheetDetent.cpp +26 -0
- package/nitrogen/generated/android/c++/JSheetDetent.hpp +42 -37
- package/nitrogen/generated/android/c++/views/JHybridNitroModalStateUpdater.cpp +20 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromodal/HybridNitroModalSpec.kt +40 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromodal/NamedSheetDetent.kt +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromodal/SheetDetent.kt +47 -10
- package/nitrogen/generated/android/nitromodal+autolinking.cmake +1 -0
- package/nitrogen/generated/ios/NitroModal-Swift-Cxx-Bridge.hpp +53 -8
- package/nitrogen/generated/ios/NitroModal-Swift-Cxx-Umbrella.hpp +4 -3
- package/nitrogen/generated/ios/c++/HybridNitroModalSpecSwift.hpp +32 -5
- package/nitrogen/generated/ios/c++/views/HybridNitroModalComponent.mm +24 -0
- package/nitrogen/generated/ios/swift/HybridNitroModalSpec.swift +4 -0
- package/nitrogen/generated/ios/swift/HybridNitroModalSpec_cxx.swift +110 -5
- package/nitrogen/generated/ios/swift/NamedSheetDetent.swift +48 -0
- package/nitrogen/generated/ios/swift/SheetDetent.swift +15 -33
- package/nitrogen/generated/shared/c++/HybridNitroModalSpec.cpp +8 -0
- package/nitrogen/generated/shared/c++/HybridNitroModalSpec.hpp +14 -5
- package/nitrogen/generated/shared/c++/{SheetDetent.hpp → NamedSheetDetent.hpp} +17 -17
- package/nitrogen/generated/shared/c++/views/HybridNitroModalComponent.cpp +9 -1
- package/nitrogen/generated/shared/c++/views/HybridNitroModalComponent.hpp +15 -2
- package/nitrogen/generated/shared/json/NitroModalConfig.json +4 -0
- package/package.json +1 -1
- package/src/NitroModal.nitro.ts +30 -2
- package/src/NitroModal.tsx +68 -4
- package/src/NitroModalView.web.tsx +54 -12
- package/src/index.tsx +2 -0
- package/src/web/AnimatedValue.ts +4 -0
- package/src/web/ModalConfig.ts +13 -3
- package/src/web/ModalController.ts +35 -2
- package/src/web/ModalPresenter.ts +46 -11
- package/src/web/SheetPresenter.ts +98 -33
- package/src/web/dom.ts +17 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
///
|
|
2
|
-
///
|
|
2
|
+
/// NamedSheetDetent.hpp
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
5
|
/// Copyright © Marc Rousavy @ Margelo
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
namespace margelo::nitro::nitromodal {
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* An enum which can be represented as a JavaScript union (
|
|
29
|
+
* An enum which can be represented as a JavaScript union (NamedSheetDetent).
|
|
30
30
|
*/
|
|
31
|
-
enum class
|
|
31
|
+
enum class NamedSheetDetent {
|
|
32
32
|
SMALL SWIFT_NAME(small) = 0,
|
|
33
33
|
MEDIUM SWIFT_NAME(medium) = 1,
|
|
34
34
|
LARGE SWIFT_NAME(large) = 2,
|
|
@@ -39,28 +39,28 @@ namespace margelo::nitro::nitromodal {
|
|
|
39
39
|
|
|
40
40
|
namespace margelo::nitro {
|
|
41
41
|
|
|
42
|
-
// C++
|
|
42
|
+
// C++ NamedSheetDetent <> JS NamedSheetDetent (union)
|
|
43
43
|
template <>
|
|
44
|
-
struct JSIConverter<margelo::nitro::nitromodal::
|
|
45
|
-
static inline margelo::nitro::nitromodal::
|
|
44
|
+
struct JSIConverter<margelo::nitro::nitromodal::NamedSheetDetent> final {
|
|
45
|
+
static inline margelo::nitro::nitromodal::NamedSheetDetent fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
46
46
|
std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, arg);
|
|
47
47
|
switch (hashString(unionValue.c_str(), unionValue.size())) {
|
|
48
|
-
case hashString("small"): return margelo::nitro::nitromodal::
|
|
49
|
-
case hashString("medium"): return margelo::nitro::nitromodal::
|
|
50
|
-
case hashString("large"): return margelo::nitro::nitromodal::
|
|
51
|
-
case hashString("fitContent"): return margelo::nitro::nitromodal::
|
|
48
|
+
case hashString("small"): return margelo::nitro::nitromodal::NamedSheetDetent::SMALL;
|
|
49
|
+
case hashString("medium"): return margelo::nitro::nitromodal::NamedSheetDetent::MEDIUM;
|
|
50
|
+
case hashString("large"): return margelo::nitro::nitromodal::NamedSheetDetent::LARGE;
|
|
51
|
+
case hashString("fitContent"): return margelo::nitro::nitromodal::NamedSheetDetent::FITCONTENT;
|
|
52
52
|
default: [[unlikely]]
|
|
53
|
-
throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum
|
|
53
|
+
throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum NamedSheetDetent - invalid value!");
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitromodal::
|
|
56
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitromodal::NamedSheetDetent arg) {
|
|
57
57
|
switch (arg) {
|
|
58
|
-
case margelo::nitro::nitromodal::
|
|
59
|
-
case margelo::nitro::nitromodal::
|
|
60
|
-
case margelo::nitro::nitromodal::
|
|
61
|
-
case margelo::nitro::nitromodal::
|
|
58
|
+
case margelo::nitro::nitromodal::NamedSheetDetent::SMALL: return JSIConverter<std::string>::toJSI(runtime, "small");
|
|
59
|
+
case margelo::nitro::nitromodal::NamedSheetDetent::MEDIUM: return JSIConverter<std::string>::toJSI(runtime, "medium");
|
|
60
|
+
case margelo::nitro::nitromodal::NamedSheetDetent::LARGE: return JSIConverter<std::string>::toJSI(runtime, "large");
|
|
61
|
+
case margelo::nitro::nitromodal::NamedSheetDetent::FITCONTENT: return JSIConverter<std::string>::toJSI(runtime, "fitContent");
|
|
62
62
|
default: [[unlikely]]
|
|
63
|
-
throw std::invalid_argument("Cannot convert
|
|
63
|
+
throw std::invalid_argument("Cannot convert NamedSheetDetent to JS - invalid value: "
|
|
64
64
|
+ std::to_string(static_cast<int>(arg)) + "!");
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -22,7 +22,8 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
22
22
|
react::ViewProps(context, sourceProps, rawProps, filterObjectKeys),
|
|
23
23
|
isOpen(nitro::ReactProp<bool>::fromRawValue("NitroModal", "isOpen", rawProps, sourceProps.isOpen)),
|
|
24
24
|
mode(nitro::ReactProp<ModalMode>::fromRawValue("NitroModal", "mode", rawProps, sourceProps.mode)),
|
|
25
|
-
|
|
25
|
+
isInline(nitro::ReactProp<bool>::fromRawValue("NitroModal", "isInline", rawProps, sourceProps.isInline)),
|
|
26
|
+
detents(nitro::ReactProp<std::vector<std::variant<NamedSheetDetent, double>>>::fromRawValue("NitroModal", "detents", rawProps, sourceProps.detents)),
|
|
26
27
|
initialDetentIndex(nitro::ReactProp<double>::fromRawValue("NitroModal", "initialDetentIndex", rawProps, sourceProps.initialDetentIndex)),
|
|
27
28
|
backdropColor(nitro::ReactProp<double>::fromRawValue("NitroModal", "backdropColor", rawProps, sourceProps.backdropColor)),
|
|
28
29
|
backdropOpacity(nitro::ReactProp<double>::fromRawValue("NitroModal", "backdropOpacity", rawProps, sourceProps.backdropOpacity)),
|
|
@@ -35,18 +36,22 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
35
36
|
contentBackgroundColor(nitro::ReactProp<std::optional<double>>::fromRawValue("NitroModal", "contentBackgroundColor", rawProps, sourceProps.contentBackgroundColor)),
|
|
36
37
|
keyboardBehavior(nitro::ReactProp<KeyboardBehavior>::fromRawValue("NitroModal", "keyboardBehavior", rawProps, sourceProps.keyboardBehavior)),
|
|
37
38
|
popupAnimation(nitro::ReactProp<PopupAnimation>::fromRawValue("NitroModal", "popupAnimation", rawProps, sourceProps.popupAnimation)),
|
|
39
|
+
pullToRefreshEnabled(nitro::ReactProp<bool>::fromRawValue("NitroModal", "pullToRefreshEnabled", rawProps, sourceProps.pullToRefreshEnabled)),
|
|
38
40
|
onPresent(nitro::ReactProp<std::optional<std::function<void()>>>::fromRawValue("NitroModal", "onPresent", rawProps, sourceProps.onPresent)),
|
|
39
41
|
onDismiss(nitro::ReactProp<std::optional<std::function<void(DismissReason /* reason */)>>>::fromRawValue("NitroModal", "onDismiss", rawProps, sourceProps.onDismiss)),
|
|
40
42
|
onDetentChange(nitro::ReactProp<std::optional<std::function<void(double /* index */)>>>::fromRawValue("NitroModal", "onDetentChange", rawProps, sourceProps.onDetentChange)),
|
|
41
43
|
onBackdropPress(nitro::ReactProp<std::optional<std::function<void()>>>::fromRawValue("NitroModal", "onBackdropPress", rawProps, sourceProps.onBackdropPress)),
|
|
42
44
|
onBackButtonPress(nitro::ReactProp<std::optional<std::function<void()>>>::fromRawValue("NitroModal", "onBackButtonPress", rawProps, sourceProps.onBackButtonPress)),
|
|
43
45
|
onContentAreaChange(nitro::ReactProp<std::optional<std::function<void(const ModalContentArea& /* area */)>>>::fromRawValue("NitroModal", "onContentAreaChange", rawProps, sourceProps.onContentAreaChange)),
|
|
46
|
+
onPullToRefresh(nitro::ReactProp<std::optional<std::function<void()>>>::fromRawValue("NitroModal", "onPullToRefresh", rawProps, sourceProps.onPullToRefresh)),
|
|
47
|
+
onRestingTopChange(nitro::ReactProp<std::optional<std::function<void(double /* top */)>>>::fromRawValue("NitroModal", "onRestingTopChange", rawProps, sourceProps.onRestingTopChange)),
|
|
44
48
|
hybridRef(nitro::ReactProp<std::optional<std::function<void(const std::shared_ptr<HybridNitroModalSpec>& /* ref */)>>>::fromRawValue("NitroModal", "hybridRef", rawProps, sourceProps.hybridRef)) { }
|
|
45
49
|
|
|
46
50
|
bool HybridNitroModalProps::filterObjectKeys(const std::string& propName) {
|
|
47
51
|
switch (hashString(propName)) {
|
|
48
52
|
case hashString("isOpen"): return true;
|
|
49
53
|
case hashString("mode"): return true;
|
|
54
|
+
case hashString("isInline"): return true;
|
|
50
55
|
case hashString("detents"): return true;
|
|
51
56
|
case hashString("initialDetentIndex"): return true;
|
|
52
57
|
case hashString("backdropColor"): return true;
|
|
@@ -60,12 +65,15 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
60
65
|
case hashString("contentBackgroundColor"): return true;
|
|
61
66
|
case hashString("keyboardBehavior"): return true;
|
|
62
67
|
case hashString("popupAnimation"): return true;
|
|
68
|
+
case hashString("pullToRefreshEnabled"): return true;
|
|
63
69
|
case hashString("onPresent"): return true;
|
|
64
70
|
case hashString("onDismiss"): return true;
|
|
65
71
|
case hashString("onDetentChange"): return true;
|
|
66
72
|
case hashString("onBackdropPress"): return true;
|
|
67
73
|
case hashString("onBackButtonPress"): return true;
|
|
68
74
|
case hashString("onContentAreaChange"): return true;
|
|
75
|
+
case hashString("onPullToRefresh"): return true;
|
|
76
|
+
case hashString("onRestingTopChange"): return true;
|
|
69
77
|
case hashString("hybridRef"): return true;
|
|
70
78
|
default: return false;
|
|
71
79
|
}
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
#include <string>
|
|
19
19
|
|
|
20
20
|
#include "ModalMode.hpp"
|
|
21
|
-
#include "
|
|
21
|
+
#include "NamedSheetDetent.hpp"
|
|
22
|
+
#include <variant>
|
|
22
23
|
#include <vector>
|
|
23
24
|
#include <optional>
|
|
24
25
|
#include "KeyboardBehavior.hpp"
|
|
@@ -51,7 +52,8 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
51
52
|
public:
|
|
52
53
|
nitro::ReactProp<bool> isOpen;
|
|
53
54
|
nitro::ReactProp<ModalMode> mode;
|
|
54
|
-
nitro::ReactProp<
|
|
55
|
+
nitro::ReactProp<bool> isInline;
|
|
56
|
+
nitro::ReactProp<std::vector<std::variant<NamedSheetDetent, double>>> detents;
|
|
55
57
|
nitro::ReactProp<double> initialDetentIndex;
|
|
56
58
|
nitro::ReactProp<double> backdropColor;
|
|
57
59
|
nitro::ReactProp<double> backdropOpacity;
|
|
@@ -64,18 +66,22 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
64
66
|
nitro::ReactProp<std::optional<double>> contentBackgroundColor;
|
|
65
67
|
nitro::ReactProp<KeyboardBehavior> keyboardBehavior;
|
|
66
68
|
nitro::ReactProp<PopupAnimation> popupAnimation;
|
|
69
|
+
nitro::ReactProp<bool> pullToRefreshEnabled;
|
|
67
70
|
nitro::ReactProp<std::optional<std::function<void()>>> onPresent;
|
|
68
71
|
nitro::ReactProp<std::optional<std::function<void(DismissReason /* reason */)>>> onDismiss;
|
|
69
72
|
nitro::ReactProp<std::optional<std::function<void(double /* index */)>>> onDetentChange;
|
|
70
73
|
nitro::ReactProp<std::optional<std::function<void()>>> onBackdropPress;
|
|
71
74
|
nitro::ReactProp<std::optional<std::function<void()>>> onBackButtonPress;
|
|
72
75
|
nitro::ReactProp<std::optional<std::function<void(const ModalContentArea& /* area */)>>> onContentAreaChange;
|
|
76
|
+
nitro::ReactProp<std::optional<std::function<void()>>> onPullToRefresh;
|
|
77
|
+
nitro::ReactProp<std::optional<std::function<void(double /* top */)>>> onRestingTopChange;
|
|
73
78
|
nitro::ReactProp<std::optional<std::function<void(const std::shared_ptr<HybridNitroModalSpec>& /* ref */)>>> hybridRef;
|
|
74
79
|
|
|
75
80
|
[[nodiscard]]
|
|
76
81
|
bool hasSameProps(const HybridNitroModalProps& other) const noexcept {
|
|
77
82
|
return isOpen.hasSameValue(other.isOpen) &&
|
|
78
83
|
mode.hasSameValue(other.mode) &&
|
|
84
|
+
isInline.hasSameValue(other.isInline) &&
|
|
79
85
|
detents.hasSameValue(other.detents) &&
|
|
80
86
|
initialDetentIndex.hasSameValue(other.initialDetentIndex) &&
|
|
81
87
|
backdropColor.hasSameValue(other.backdropColor) &&
|
|
@@ -89,12 +95,15 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
89
95
|
contentBackgroundColor.hasSameValue(other.contentBackgroundColor) &&
|
|
90
96
|
keyboardBehavior.hasSameValue(other.keyboardBehavior) &&
|
|
91
97
|
popupAnimation.hasSameValue(other.popupAnimation) &&
|
|
98
|
+
pullToRefreshEnabled.hasSameValue(other.pullToRefreshEnabled) &&
|
|
92
99
|
onPresent.hasSameValue(other.onPresent) &&
|
|
93
100
|
onDismiss.hasSameValue(other.onDismiss) &&
|
|
94
101
|
onDetentChange.hasSameValue(other.onDetentChange) &&
|
|
95
102
|
onBackdropPress.hasSameValue(other.onBackdropPress) &&
|
|
96
103
|
onBackButtonPress.hasSameValue(other.onBackButtonPress) &&
|
|
97
104
|
onContentAreaChange.hasSameValue(other.onContentAreaChange) &&
|
|
105
|
+
onPullToRefresh.hasSameValue(other.onPullToRefresh) &&
|
|
106
|
+
onRestingTopChange.hasSameValue(other.onRestingTopChange) &&
|
|
98
107
|
hybridRef.hasSameValue(other.hybridRef);
|
|
99
108
|
}
|
|
100
109
|
|
|
@@ -102,6 +111,7 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
102
111
|
bool hasAnyProvidedProps() const noexcept {
|
|
103
112
|
return isOpen.isProvided() ||
|
|
104
113
|
mode.isProvided() ||
|
|
114
|
+
isInline.isProvided() ||
|
|
105
115
|
detents.isProvided() ||
|
|
106
116
|
initialDetentIndex.isProvided() ||
|
|
107
117
|
backdropColor.isProvided() ||
|
|
@@ -115,12 +125,15 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
115
125
|
contentBackgroundColor.isProvided() ||
|
|
116
126
|
keyboardBehavior.isProvided() ||
|
|
117
127
|
popupAnimation.isProvided() ||
|
|
128
|
+
pullToRefreshEnabled.isProvided() ||
|
|
118
129
|
onPresent.isProvided() ||
|
|
119
130
|
onDismiss.isProvided() ||
|
|
120
131
|
onDetentChange.isProvided() ||
|
|
121
132
|
onBackdropPress.isProvided() ||
|
|
122
133
|
onBackButtonPress.isProvided() ||
|
|
123
134
|
onContentAreaChange.isProvided() ||
|
|
135
|
+
onPullToRefresh.isProvided() ||
|
|
136
|
+
onRestingTopChange.isProvided() ||
|
|
124
137
|
hybridRef.isProvided();
|
|
125
138
|
}
|
|
126
139
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"validAttributes": {
|
|
7
7
|
"isOpen": true,
|
|
8
8
|
"mode": true,
|
|
9
|
+
"isInline": true,
|
|
9
10
|
"detents": true,
|
|
10
11
|
"initialDetentIndex": true,
|
|
11
12
|
"backdropColor": true,
|
|
@@ -19,12 +20,15 @@
|
|
|
19
20
|
"contentBackgroundColor": true,
|
|
20
21
|
"keyboardBehavior": true,
|
|
21
22
|
"popupAnimation": true,
|
|
23
|
+
"pullToRefreshEnabled": true,
|
|
22
24
|
"onPresent": true,
|
|
23
25
|
"onDismiss": true,
|
|
24
26
|
"onDetentChange": true,
|
|
25
27
|
"onBackdropPress": true,
|
|
26
28
|
"onBackButtonPress": true,
|
|
27
29
|
"onContentAreaChange": true,
|
|
30
|
+
"onPullToRefresh": true,
|
|
31
|
+
"onRestingTopChange": true,
|
|
28
32
|
"hybridRef": true
|
|
29
33
|
}
|
|
30
34
|
}
|
package/package.json
CHANGED
package/src/NitroModal.nitro.ts
CHANGED
|
@@ -13,13 +13,20 @@ import type {
|
|
|
13
13
|
export type ModalMode = 'bottomSheet' | 'popup';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Named resting heights of a `bottomSheet`.
|
|
17
17
|
* - `small`: ~25% of the available height.
|
|
18
18
|
* - `medium`: ~50% of the available height.
|
|
19
19
|
* - `large`: the full available height.
|
|
20
20
|
* - `fitContent`: the measured height of the React content.
|
|
21
21
|
*/
|
|
22
|
-
export type
|
|
22
|
+
export type NamedSheetDetent = 'small' | 'medium' | 'large' | 'fitContent';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A resting height of a `bottomSheet`: a named height, or the height
|
|
26
|
+
* (dp/pt) of the content above the bottom safe area, clamped to the
|
|
27
|
+
* available height.
|
|
28
|
+
*/
|
|
29
|
+
export type SheetDetent = NamedSheetDetent | number;
|
|
23
30
|
|
|
24
31
|
/**
|
|
25
32
|
* How the modal reacts to the software keyboard.
|
|
@@ -48,6 +55,12 @@ export interface NitroModalProps extends HybridViewProps {
|
|
|
48
55
|
/** Whether the modal should be presented. */
|
|
49
56
|
isOpen: boolean;
|
|
50
57
|
mode: ModalMode;
|
|
58
|
+
/**
|
|
59
|
+
* A `bottomSheet` that lives inside the host view's own bounds instead of
|
|
60
|
+
* being presented over the app: no backdrop, touches outside the sheet
|
|
61
|
+
* reach the views behind it, and the user can't dismiss it.
|
|
62
|
+
*/
|
|
63
|
+
isInline: boolean;
|
|
51
64
|
/** Detents of a `bottomSheet`, smallest first. At most 3 are used on Android. */
|
|
52
65
|
detents: SheetDetent[];
|
|
53
66
|
/** Index into `detents` that the sheet opens at. */
|
|
@@ -68,6 +81,11 @@ export interface NitroModalProps extends HybridViewProps {
|
|
|
68
81
|
contentBackgroundColor?: number;
|
|
69
82
|
keyboardBehavior: KeyboardBehavior;
|
|
70
83
|
popupAnimation: PopupAnimation;
|
|
84
|
+
/**
|
|
85
|
+
* Pulling a `bottomSheet` down past its lowest detent calls
|
|
86
|
+
* `onPullToRefresh`. Needs `dismissOnSwipe` off (always the case inline).
|
|
87
|
+
*/
|
|
88
|
+
pullToRefreshEnabled: boolean;
|
|
71
89
|
|
|
72
90
|
/** The present transition finished. */
|
|
73
91
|
onPresent?: () => void;
|
|
@@ -80,6 +98,16 @@ export interface NitroModalProps extends HybridViewProps {
|
|
|
80
98
|
onBackButtonPress?: () => void;
|
|
81
99
|
/** The area available to the content changed (rotation, keyboard, sheet sizing). */
|
|
82
100
|
onContentAreaChange?: (area: ModalContentArea) => void;
|
|
101
|
+
/**
|
|
102
|
+
* The user pulled the sheet down past its lowest detent and let go. The
|
|
103
|
+
* gesture must start with the sheet resting on that detent.
|
|
104
|
+
*/
|
|
105
|
+
onPullToRefresh?: () => void;
|
|
106
|
+
/**
|
|
107
|
+
* Inline sheets: where the sheet's top rests within the host view (or
|
|
108
|
+
* will, once it settles). Lets React lay the content out where it is shown.
|
|
109
|
+
*/
|
|
110
|
+
onRestingTopChange?: (top: number) => void;
|
|
83
111
|
}
|
|
84
112
|
|
|
85
113
|
export interface NitroModalMethods extends HybridViewMethods {
|
package/src/NitroModal.tsx
CHANGED
|
@@ -21,12 +21,22 @@ import type {
|
|
|
21
21
|
KeyboardBehavior,
|
|
22
22
|
ModalContentArea,
|
|
23
23
|
ModalMode,
|
|
24
|
+
NamedSheetDetent,
|
|
24
25
|
NitroModal as NitroModalHybridView,
|
|
25
26
|
PopupAnimation,
|
|
26
27
|
SheetDetent,
|
|
27
28
|
} from './NitroModal.nitro';
|
|
28
29
|
import { callback, NitroModalView } from './NitroModalView';
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Where a `bottomSheet` lives.
|
|
33
|
+
* - `modal`: presented over the whole app, with a backdrop.
|
|
34
|
+
* - `inline`: inside the component's own frame (`style`), as part of your
|
|
35
|
+
* screen. No backdrop, the views behind it stay interactive, and the user
|
|
36
|
+
* can't dismiss it.
|
|
37
|
+
*/
|
|
38
|
+
export type ModalPresentation = 'modal' | 'inline';
|
|
39
|
+
|
|
30
40
|
export interface NitroModalRef {
|
|
31
41
|
/** Opens the modal. Only for uncontrolled usage (no `isOpen` prop). */
|
|
32
42
|
present(): void;
|
|
@@ -45,7 +55,23 @@ export interface NitroModalProps {
|
|
|
45
55
|
isOpen?: boolean;
|
|
46
56
|
/** @default 'bottomSheet' */
|
|
47
57
|
mode?: ModalMode;
|
|
48
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* `inline` keeps a `bottomSheet` inside this component's frame, like a
|
|
60
|
+
* persistent sheet that is part of the screen. Popups are always modal.
|
|
61
|
+
* @default 'modal'
|
|
62
|
+
*/
|
|
63
|
+
presentation?: ModalPresentation;
|
|
64
|
+
/**
|
|
65
|
+
* Inline only: the frame the sheet lives in, laid out like any view.
|
|
66
|
+
* The sheet rests against its bottom edge and its largest detent reaches its
|
|
67
|
+
* top edge. @default StyleSheet.absoluteFill
|
|
68
|
+
*/
|
|
69
|
+
style?: StyleProp<ViewStyle>;
|
|
70
|
+
/**
|
|
71
|
+
* Sheet heights, smallest first: named sizes, or the content height in
|
|
72
|
+
* dp/pt above the bottom safe area. Android uses at most three.
|
|
73
|
+
* @default ['fitContent']
|
|
74
|
+
*/
|
|
49
75
|
detents?: SheetDetent[];
|
|
50
76
|
/** @default 0 */
|
|
51
77
|
initialDetentIndex?: number;
|
|
@@ -78,6 +104,13 @@ export interface NitroModalProps {
|
|
|
78
104
|
onDetentChange?: (index: number) => void;
|
|
79
105
|
onBackdropPress?: () => void;
|
|
80
106
|
onBackButtonPress?: () => void;
|
|
107
|
+
/**
|
|
108
|
+
* Pull-to-refresh: the user dragged the sheet down past its lowest detent
|
|
109
|
+
* and let go. Only for sheets that can't be swiped away (inline, or
|
|
110
|
+
* `dismissOnSwipe={false}`), and only from a gesture that starts with the
|
|
111
|
+
* sheet resting on its lowest detent.
|
|
112
|
+
*/
|
|
113
|
+
onPullToRefresh?: () => void;
|
|
81
114
|
children?: ReactNode;
|
|
82
115
|
testID?: string;
|
|
83
116
|
ref?: Ref<NitroModalRef>;
|
|
@@ -86,6 +119,13 @@ export interface NitroModalProps {
|
|
|
86
119
|
const DEFAULT_DETENTS: SheetDetent[] = ['fitContent'];
|
|
87
120
|
const BLACK = 0xff000000;
|
|
88
121
|
|
|
122
|
+
function parseDetent(value: string): SheetDetent {
|
|
123
|
+
const points = Number(value);
|
|
124
|
+
return value !== '' && Number.isFinite(points)
|
|
125
|
+
? points
|
|
126
|
+
: (value as NamedSheetDetent);
|
|
127
|
+
}
|
|
128
|
+
|
|
89
129
|
function toNativeColor(color: ColorValue | undefined): number | undefined {
|
|
90
130
|
if (color == null) return undefined;
|
|
91
131
|
const processed = processColor(color);
|
|
@@ -96,6 +136,8 @@ function toNativeColor(color: ColorValue | undefined): number | undefined {
|
|
|
96
136
|
export function NitroModal({
|
|
97
137
|
isOpen,
|
|
98
138
|
mode = 'bottomSheet',
|
|
139
|
+
presentation = 'modal',
|
|
140
|
+
style,
|
|
99
141
|
detents = DEFAULT_DETENTS,
|
|
100
142
|
initialDetentIndex = 0,
|
|
101
143
|
backdropColor = 'black',
|
|
@@ -115,11 +157,13 @@ export function NitroModal({
|
|
|
115
157
|
onDetentChange,
|
|
116
158
|
onBackdropPress,
|
|
117
159
|
onBackButtonPress,
|
|
160
|
+
onPullToRefresh,
|
|
118
161
|
children,
|
|
119
162
|
testID,
|
|
120
163
|
ref,
|
|
121
164
|
}: NitroModalProps) {
|
|
122
165
|
const isControlled = isOpen !== undefined;
|
|
166
|
+
const isInline = presentation === 'inline' && mode === 'bottomSheet';
|
|
123
167
|
const [internalOpen, setInternalOpen] = useState(false);
|
|
124
168
|
const open = isControlled ? isOpen : internalOpen;
|
|
125
169
|
|
|
@@ -134,6 +178,10 @@ export function NitroModal({
|
|
|
134
178
|
const renderContent = open || sessionActive;
|
|
135
179
|
|
|
136
180
|
const [area, setArea] = useState<ModalContentArea | null>(null);
|
|
181
|
+
// Inline (iOS/Android): where the sheet's top rests in the host. The content
|
|
182
|
+
// is laid out there so `measure()`, which presses rely on, matches the
|
|
183
|
+
// screen; native cancels the offset out on screen.
|
|
184
|
+
const [restingTop, setRestingTop] = useState(0);
|
|
137
185
|
const hybridRef = useRef<NitroModalHybridView | null>(null);
|
|
138
186
|
|
|
139
187
|
// Native callbacks are created once (stable props, no native updates on
|
|
@@ -145,6 +193,7 @@ export function NitroModal({
|
|
|
145
193
|
onDetentChange,
|
|
146
194
|
onBackdropPress,
|
|
147
195
|
onBackButtonPress,
|
|
196
|
+
onPullToRefresh,
|
|
148
197
|
});
|
|
149
198
|
useLayoutEffect(() => {
|
|
150
199
|
latest.current = {
|
|
@@ -154,6 +203,7 @@ export function NitroModal({
|
|
|
154
203
|
onDetentChange,
|
|
155
204
|
onBackdropPress,
|
|
156
205
|
onBackButtonPress,
|
|
206
|
+
onPullToRefresh,
|
|
157
207
|
};
|
|
158
208
|
});
|
|
159
209
|
|
|
@@ -180,6 +230,12 @@ export function NitroModal({
|
|
|
180
230
|
onBackButtonPress: callback(() => {
|
|
181
231
|
latest.current.onBackButtonPress?.();
|
|
182
232
|
}),
|
|
233
|
+
onPullToRefresh: callback(() => {
|
|
234
|
+
latest.current.onPullToRefresh?.();
|
|
235
|
+
}),
|
|
236
|
+
onRestingTopChange: callback((top: number) => {
|
|
237
|
+
setRestingTop(top);
|
|
238
|
+
}),
|
|
183
239
|
onContentAreaChange: callback((next: ModalContentArea) => {
|
|
184
240
|
setArea((current) =>
|
|
185
241
|
current?.width === next.width && current.height === next.height
|
|
@@ -220,7 +276,7 @@ export function NitroModal({
|
|
|
220
276
|
// Arrays are diffed by identity, so keep one instance per distinct value.
|
|
221
277
|
const detentsKey = detents.join(',');
|
|
222
278
|
const nativeDetents = useMemo(
|
|
223
|
-
() => [...new Set(detentsKey.split(','))]
|
|
279
|
+
() => [...new Set(detentsKey.split(','))].map(parseDetent),
|
|
224
280
|
[detentsKey]
|
|
225
281
|
);
|
|
226
282
|
|
|
@@ -240,9 +296,11 @@ export function NitroModal({
|
|
|
240
296
|
|
|
241
297
|
return (
|
|
242
298
|
<NitroModalView
|
|
243
|
-
style={styles.host}
|
|
299
|
+
style={isInline ? [StyleSheet.absoluteFill, style] : styles.host}
|
|
300
|
+
pointerEvents={isInline ? 'box-none' : undefined}
|
|
244
301
|
isOpen={open}
|
|
245
302
|
mode={mode}
|
|
303
|
+
isInline={isInline}
|
|
246
304
|
detents={nativeDetents}
|
|
247
305
|
initialDetentIndex={initialDetentIndex}
|
|
248
306
|
backdropColor={toNativeColor(backdropColor) ?? BLACK}
|
|
@@ -256,13 +314,19 @@ export function NitroModal({
|
|
|
256
314
|
contentBackgroundColor={toNativeColor(backgroundColor)}
|
|
257
315
|
keyboardBehavior={keyboardBehavior}
|
|
258
316
|
popupAnimation={popupAnimation}
|
|
317
|
+
pullToRefreshEnabled={onPullToRefresh != null}
|
|
259
318
|
{...nativeCallbacks}
|
|
260
319
|
>
|
|
261
320
|
{renderContent ? (
|
|
262
321
|
<View
|
|
263
322
|
collapsable={false}
|
|
264
323
|
testID={testID}
|
|
265
|
-
style={[
|
|
324
|
+
style={[
|
|
325
|
+
styles.container,
|
|
326
|
+
containerStyle,
|
|
327
|
+
contentContainerStyle,
|
|
328
|
+
isInline && restingTop > 0 ? { top: restingTop } : null,
|
|
329
|
+
]}
|
|
266
330
|
>
|
|
267
331
|
{children}
|
|
268
332
|
</View>
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
useCallback,
|
|
4
5
|
useLayoutEffect,
|
|
5
6
|
useRef,
|
|
6
7
|
useState,
|
|
7
8
|
type CSSProperties,
|
|
8
9
|
type ReactNode,
|
|
9
10
|
} from 'react';
|
|
10
|
-
import { Modal } from 'react-native';
|
|
11
|
+
import { Modal, View, type StyleProp, type ViewStyle } from 'react-native';
|
|
11
12
|
import type { ReactNativeView } from 'react-native-nitro-modules';
|
|
12
13
|
import type { NitroModalMethods, NitroModalProps } from './NitroModal.nitro';
|
|
14
|
+
import { clipOverflow } from './web/dom';
|
|
13
15
|
import { ModalController } from './web/ModalController';
|
|
14
16
|
|
|
15
17
|
interface WebNitroModalViewProps extends NitroModalProps {
|
|
16
18
|
hybridRef?: (instance: NitroModalMethods) => void;
|
|
19
|
+
/** Inline: the frame the sheet fills. */
|
|
20
|
+
style?: StyleProp<ViewStyle>;
|
|
17
21
|
children?: ReactNode;
|
|
18
22
|
}
|
|
19
23
|
|
|
@@ -27,9 +31,13 @@ export function callback<T>(func: T): T {
|
|
|
27
31
|
* Native Web's `Modal` (a portal with focus trapping, Escape handling and
|
|
28
32
|
* stacking with other modals) and is laid out and animated by the presenters
|
|
29
33
|
* in `./web`. In place, it renders nothing.
|
|
34
|
+
*
|
|
35
|
+
* An inline sheet renders in place instead: a view laid out with `style`,
|
|
36
|
+
* holding the sheet's elements.
|
|
30
37
|
*/
|
|
31
38
|
function WebNitroModalView(props: WebNitroModalViewProps) {
|
|
32
39
|
const { hybridRef, children } = props;
|
|
40
|
+
const isInline = props.isInline && props.mode === 'bottomSheet';
|
|
33
41
|
const [mounted, setMounted] = useState(false);
|
|
34
42
|
const [controller] = useState(() => new ModalController(setMounted));
|
|
35
43
|
const overlay = useRef<HTMLDivElement>(null);
|
|
@@ -69,10 +77,46 @@ function WebNitroModalView(props: WebNitroModalViewProps) {
|
|
|
69
77
|
}
|
|
70
78
|
});
|
|
71
79
|
|
|
72
|
-
|
|
80
|
+
const hostRef = useCallback(
|
|
81
|
+
(node: unknown) => {
|
|
82
|
+
controller.hostDidChange(node instanceof HTMLElement ? node : null);
|
|
83
|
+
},
|
|
84
|
+
[controller]
|
|
85
|
+
);
|
|
73
86
|
|
|
74
87
|
// Styles set here never change; the presenters write sizes, positions,
|
|
75
88
|
// colors and animated values straight to the elements.
|
|
89
|
+
const layer = mounted ? (
|
|
90
|
+
// Focusable so the modal's focus trap lands here first rather than on the first input.
|
|
91
|
+
<div
|
|
92
|
+
ref={overlay}
|
|
93
|
+
tabIndex={-1}
|
|
94
|
+
style={
|
|
95
|
+
isInline
|
|
96
|
+
? { ...styles.inlineOverlay, overflow: clipOverflow() }
|
|
97
|
+
: styles.overlay
|
|
98
|
+
}
|
|
99
|
+
>
|
|
100
|
+
<div ref={backdrop} aria-hidden style={styles.backdrop} />
|
|
101
|
+
<div ref={surface} style={styles.surface}>
|
|
102
|
+
<div ref={content} style={styles.content}>
|
|
103
|
+
{children}
|
|
104
|
+
</div>
|
|
105
|
+
<div ref={grabber} aria-hidden style={styles.grabber} />
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
) : null;
|
|
109
|
+
|
|
110
|
+
if (isInline) {
|
|
111
|
+
return (
|
|
112
|
+
<View ref={hostRef} style={props.style} pointerEvents="box-none">
|
|
113
|
+
{layer}
|
|
114
|
+
</View>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!layer) return null;
|
|
119
|
+
|
|
76
120
|
return (
|
|
77
121
|
<Modal
|
|
78
122
|
visible
|
|
@@ -80,16 +124,7 @@ function WebNitroModalView(props: WebNitroModalViewProps) {
|
|
|
80
124
|
animationType="none"
|
|
81
125
|
onRequestClose={controller.handleBackPress}
|
|
82
126
|
>
|
|
83
|
-
{
|
|
84
|
-
<div ref={overlay} tabIndex={-1} style={styles.overlay}>
|
|
85
|
-
<div ref={backdrop} aria-hidden style={styles.backdrop} />
|
|
86
|
-
<div ref={surface} style={styles.surface}>
|
|
87
|
-
<div ref={content} style={styles.content}>
|
|
88
|
-
{children}
|
|
89
|
-
</div>
|
|
90
|
-
<div ref={grabber} aria-hidden style={styles.grabber} />
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
127
|
+
{layer}
|
|
93
128
|
</Modal>
|
|
94
129
|
);
|
|
95
130
|
}
|
|
@@ -100,6 +135,13 @@ const styles = {
|
|
|
100
135
|
inset: 0,
|
|
101
136
|
outline: 'none',
|
|
102
137
|
},
|
|
138
|
+
// Fills the host view; the sheet hides below its bottom edge.
|
|
139
|
+
inlineOverlay: {
|
|
140
|
+
position: 'absolute',
|
|
141
|
+
inset: 0,
|
|
142
|
+
outline: 'none',
|
|
143
|
+
pointerEvents: 'none',
|
|
144
|
+
},
|
|
103
145
|
backdrop: {
|
|
104
146
|
position: 'absolute',
|
|
105
147
|
inset: 0,
|
package/src/index.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export {
|
|
2
2
|
NitroModal,
|
|
3
|
+
type ModalPresentation,
|
|
3
4
|
type NitroModalProps,
|
|
4
5
|
type NitroModalRef,
|
|
5
6
|
} from './NitroModal';
|
|
@@ -7,6 +8,7 @@ export type {
|
|
|
7
8
|
DismissReason,
|
|
8
9
|
KeyboardBehavior,
|
|
9
10
|
ModalMode,
|
|
11
|
+
NamedSheetDetent,
|
|
10
12
|
PopupAnimation,
|
|
11
13
|
SheetDetent,
|
|
12
14
|
} from './NitroModal.nitro';
|
package/src/web/AnimatedValue.ts
CHANGED
package/src/web/ModalConfig.ts
CHANGED
|
@@ -9,6 +9,11 @@ import type {
|
|
|
9
9
|
/** Snapshot of the props that shape a presentation. */
|
|
10
10
|
export interface ModalConfig {
|
|
11
11
|
mode: ModalMode;
|
|
12
|
+
/**
|
|
13
|
+
* A `bottomSheet` rendered in place, filling the host view: no backdrop,
|
|
14
|
+
* the page behind stays interactive, and the user can't dismiss it.
|
|
15
|
+
*/
|
|
16
|
+
isInline: boolean;
|
|
12
17
|
detents: SheetDetent[];
|
|
13
18
|
initialDetentIndex: number;
|
|
14
19
|
/** 0xAARRGGBB, from `processColor`. */
|
|
@@ -25,11 +30,15 @@ export interface ModalConfig {
|
|
|
25
30
|
contentBackgroundColor: number | null;
|
|
26
31
|
keyboardBehavior: KeyboardBehavior;
|
|
27
32
|
popupAnimation: PopupAnimation;
|
|
33
|
+
pullToRefreshEnabled: boolean;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
export function makeConfig(props: NitroModalProps): ModalConfig {
|
|
37
|
+
// An inline sheet is part of the page; the user can't dismiss it.
|
|
38
|
+
const isInline = props.isInline && props.mode === 'bottomSheet';
|
|
31
39
|
return {
|
|
32
40
|
mode: props.mode,
|
|
41
|
+
isInline,
|
|
33
42
|
detents: props.detents,
|
|
34
43
|
initialDetentIndex: Number.isFinite(props.initialDetentIndex)
|
|
35
44
|
? Math.trunc(props.initialDetentIndex)
|
|
@@ -37,14 +46,15 @@ export function makeConfig(props: NitroModalProps): ModalConfig {
|
|
|
37
46
|
backdropColor: props.backdropColor,
|
|
38
47
|
backdropOpacity: props.backdropOpacity,
|
|
39
48
|
backdropBlurRadius: props.backdropBlurRadius,
|
|
40
|
-
dismissOnBackdropPress: props.dismissOnBackdropPress,
|
|
41
|
-
dismissOnSwipe: props.dismissOnSwipe,
|
|
42
|
-
dismissOnBackButton: props.dismissOnBackButton,
|
|
49
|
+
dismissOnBackdropPress: props.dismissOnBackdropPress && !isInline,
|
|
50
|
+
dismissOnSwipe: props.dismissOnSwipe && !isInline,
|
|
51
|
+
dismissOnBackButton: props.dismissOnBackButton && !isInline,
|
|
43
52
|
grabberVisible: props.grabberVisible,
|
|
44
53
|
cornerRadius: props.cornerRadius >= 0 ? props.cornerRadius : null,
|
|
45
54
|
contentBackgroundColor: props.contentBackgroundColor ?? null,
|
|
46
55
|
keyboardBehavior: props.keyboardBehavior,
|
|
47
56
|
popupAnimation: props.popupAnimation,
|
|
57
|
+
pullToRefreshEnabled: props.pullToRefreshEnabled,
|
|
48
58
|
};
|
|
49
59
|
}
|
|
50
60
|
|