@rn-tools/sheets 0.1.4 → 3.0.2
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/CHANGELOG.md +16 -0
- package/README.md +107 -67
- package/android/src/main/java/expo/modules/sheets/RNToolsSheetsModule.kt +8 -4
- package/android/src/main/java/expo/modules/sheets/RNToolsSheetsView.kt +110 -76
- package/android/src/main/java/expo/modules/sheets/SheetProps.kt +2 -1
- package/ios/RNToolsSheets.podspec +3 -3
- package/ios/RNToolsSheetsModule.swift +27 -10
- package/ios/RNToolsSheetsView.swift +269 -224
- package/ios/Sources/RNToolsTouchHandlerHelper.h +15 -0
- package/ios/Sources/RNToolsTouchHandlerHelper.mm +31 -0
- package/mocks/expo-modules-core.mock.ts +9 -0
- package/package.json +10 -14
- package/src/index.ts +4 -1
- package/src/native-sheets-view.tsx +126 -42
- package/src/sheet-slot.tsx +70 -0
- package/src/sheets-client.test.tsx +239 -0
- package/src/sheets-client.tsx +233 -0
- package/src/sheets-provider.tsx +20 -0
- package/vitest.config.mts +25 -0
- package/ios/Sources/RNTSurfaceTouchHandlerWrapper.h +0 -11
- package/ios/Sources/RNTSurfaceTouchHandlerWrapper.mm +0 -43
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 723c379: Incorporate sheets into navigation and cleanup APIs
|
|
8
|
+
- Updated dependencies [723c379]
|
|
9
|
+
- @rn-tools/core@3.0.2
|
|
10
|
+
|
|
11
|
+
## 3.0.1
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- e62a766: Initial changeset publish
|
|
16
|
+
- Updated dependencies [e62a766]
|
|
17
|
+
- @rn-tools/core@3.0.1
|
|
18
|
+
|
|
3
19
|
## Unpublished
|
|
4
20
|
|
|
5
21
|
### 🛠 Breaking changes
|
package/README.md
CHANGED
|
@@ -1,102 +1,142 @@
|
|
|
1
1
|
# @rn-tools/sheets
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Native bottom sheets for React Native + Expo with iOS `UISheetPresentationController` and Android `BottomSheetDialog`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
|
+
```bash
|
|
8
|
+
yarn add @rn-tools/sheets expo-build-properties
|
|
9
|
+
```
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## Motivation
|
|
12
|
-
|
|
13
|
-
- Better performance and responsiveness than JS based solutions
|
|
14
|
-
|
|
15
|
-
- Native OS handling for gestures, keyboard, and navigation
|
|
16
|
-
|
|
17
|
-
## Installation
|
|
18
|
-
|
|
19
|
-
`yarn add @rntools/sheets expo-build-properties`
|
|
20
|
-
|
|
21
|
-
Update your minimum iOS deployment target to 16 in `app.json`:
|
|
11
|
+
Set iOS deployment target to `16.0` in `app.json`:
|
|
22
12
|
|
|
23
13
|
```json
|
|
24
14
|
{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
15
|
+
"plugins": [
|
|
16
|
+
[
|
|
17
|
+
"expo-build-properties",
|
|
18
|
+
{
|
|
19
|
+
"ios": {
|
|
20
|
+
"deploymentTarget": "16.0"
|
|
32
21
|
}
|
|
33
|
-
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
]
|
|
34
25
|
}
|
|
35
|
-
|
|
36
26
|
```
|
|
37
27
|
|
|
38
|
-
|
|
28
|
+
Then rebuild the native app.
|
|
29
|
+
|
|
30
|
+
## APIs
|
|
39
31
|
|
|
32
|
+
This package supports two usage styles:
|
|
40
33
|
|
|
41
|
-
|
|
34
|
+
1. Declarative `BottomSheet`
|
|
35
|
+
2. Store-driven `createSheets` + `SheetsProvider`
|
|
36
|
+
|
|
37
|
+
### Declarative `BottomSheet`
|
|
42
38
|
|
|
43
39
|
```tsx
|
|
44
|
-
import
|
|
40
|
+
import * as React from "react";
|
|
41
|
+
import { Button, View } from "react-native";
|
|
42
|
+
import { BottomSheet } from "@rn-tools/sheets";
|
|
45
43
|
|
|
46
|
-
export default function
|
|
44
|
+
export default function Example() {
|
|
47
45
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
48
46
|
|
|
49
47
|
return (
|
|
50
|
-
<View
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
cornerRadius: 32.0,
|
|
62
|
-
backgroundColor: "#ffffff",
|
|
63
|
-
}}
|
|
64
|
-
appearanceIOS={{
|
|
65
|
-
cornerRadius: 16.0,
|
|
66
|
-
grabberVisible: true,
|
|
67
|
-
backgroundColor: "#ffffff",
|
|
68
|
-
}}
|
|
69
|
-
>
|
|
70
|
-
{isOpen && <MyContent />}
|
|
71
|
-
</BottomSheet>
|
|
48
|
+
<View style={{ flex: 1 }}>
|
|
49
|
+
<Button title="Open" onPress={() => setIsOpen(true)} />
|
|
50
|
+
|
|
51
|
+
<BottomSheet
|
|
52
|
+
isOpen={isOpen}
|
|
53
|
+
setIsOpen={setIsOpen}
|
|
54
|
+
snapPoints={[300, 500]}
|
|
55
|
+
initialIndex={0}
|
|
56
|
+
>
|
|
57
|
+
<View style={{ padding: 24 }}>{/* content */}</View>
|
|
58
|
+
</BottomSheet>
|
|
72
59
|
</View>
|
|
73
60
|
);
|
|
74
61
|
}
|
|
75
62
|
```
|
|
76
63
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
- `isOpen / onOpenChange` - Controller props for toggling the sheet open and closed - this is required
|
|
64
|
+
### Store-driven sheets
|
|
80
65
|
|
|
81
|
-
|
|
66
|
+
Use this for imperative sheet presentation from anywhere in your app.
|
|
67
|
+
You do not need a hook for this pattern; you can call the external sheets store directly.
|
|
82
68
|
|
|
83
|
-
|
|
69
|
+
```tsx
|
|
70
|
+
import * as React from "react";
|
|
71
|
+
import { Button, View } from "react-native";
|
|
72
|
+
import { createSheets, SheetsProvider } from "@rn-tools/sheets";
|
|
84
73
|
|
|
85
|
-
|
|
86
|
-
- { type: "OPEN", payload: { index: number }}
|
|
87
|
-
- { type: "SETTLING" }
|
|
88
|
-
- { type: "DRAGGING" }
|
|
74
|
+
const sheets = createSheets();
|
|
89
75
|
|
|
90
|
-
|
|
76
|
+
export default function App() {
|
|
77
|
+
return (
|
|
78
|
+
<SheetsProvider sheets={sheets}>
|
|
79
|
+
<Screen />
|
|
80
|
+
</SheetsProvider>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
91
83
|
|
|
92
|
-
|
|
84
|
+
function Screen() {
|
|
85
|
+
return (
|
|
86
|
+
<View>
|
|
87
|
+
<Button
|
|
88
|
+
title="Present"
|
|
89
|
+
onPress={() => {
|
|
90
|
+
sheets.present(<SheetContent />, {
|
|
91
|
+
id: "edit",
|
|
92
|
+
snapPoints: [320, 520],
|
|
93
|
+
});
|
|
94
|
+
}}
|
|
95
|
+
/>
|
|
96
|
+
<Button title="Dismiss" onPress={() => sheets.dismiss()} />
|
|
97
|
+
<Button title="Dismiss all" onPress={() => sheets.dismissAll()} />
|
|
98
|
+
</View>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
93
101
|
|
|
94
|
-
|
|
95
|
-
|
|
102
|
+
function SheetContent() {
|
|
103
|
+
return <View style={{ padding: 24 }} />;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
96
106
|
|
|
107
|
+
`useSheets()` is still available when you prefer resolving the client from context.
|
|
97
108
|
|
|
98
|
-
##
|
|
109
|
+
## `createSheets` client
|
|
99
110
|
|
|
100
|
-
|
|
111
|
+
```ts
|
|
112
|
+
type SheetsClient = {
|
|
113
|
+
store: SheetsStore;
|
|
114
|
+
present: (element: React.ReactElement, options?: SheetOptions) => string;
|
|
115
|
+
dismiss: (id?: string) => void;
|
|
116
|
+
dismissAll: () => void;
|
|
117
|
+
};
|
|
118
|
+
```
|
|
101
119
|
|
|
102
|
-
-
|
|
120
|
+
- `present` returns a sheet key.
|
|
121
|
+
- `options.id` lets you target a logical sheet instance.
|
|
122
|
+
- `dismiss(id?)` closes by key/id, or top-most if omitted.
|
|
123
|
+
- `dismissAll()` closes all active sheets.
|
|
124
|
+
|
|
125
|
+
## `BottomSheet` props
|
|
126
|
+
|
|
127
|
+
- `isOpen`: whether the sheet should be open.
|
|
128
|
+
- `setIsOpen(next)`: called when native requests a visibility change.
|
|
129
|
+
- `snapPoints?: number[]`: snap heights (dp). Android uses first 2 only.
|
|
130
|
+
- `initialIndex?: number`: initial snap point index.
|
|
131
|
+
- `canDismiss?: boolean`: allow swipe/back dismissal (default `true`).
|
|
132
|
+
- `onDismissPrevented?: () => void`: called when dismissal is blocked.
|
|
133
|
+
- `onStateChange?: (event) => void`: emits `{ type: "OPEN" }` and `{ type: "HIDDEN" }`.
|
|
134
|
+
- `containerStyle?: ViewStyle`
|
|
135
|
+
- `appearanceIOS?: { grabberVisible?: boolean; backgroundColor?: string; cornerRadius?: number }`
|
|
136
|
+
- `appearanceAndroid?: { dimAmount?: number; backgroundColor?: string; cornerRadius?: number }`
|
|
137
|
+
|
|
138
|
+
## Notes
|
|
139
|
+
|
|
140
|
+
- If `snapPoints` is omitted, the sheet auto-sizes to measured content height.
|
|
141
|
+
- On Android, nested scroll content should use `nestedScrollEnabled` where needed.
|
|
142
|
+
- iOS uses an overlay window to host the presented sheet.
|
|
@@ -31,17 +31,21 @@ class RNToolsSheetsModule : Module() {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
Events("onDismiss", "onStateChange")
|
|
34
|
+
Events("onDismiss", "onStateChange", "onDismissPrevented")
|
|
35
35
|
|
|
36
36
|
Prop("isOpen") { view: RNToolsSheetsView, isOpen: Boolean ->
|
|
37
37
|
view.props.isOpen = isOpen
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
Prop("
|
|
41
|
-
view.props.
|
|
40
|
+
Prop("initialIndex") { view: RNToolsSheetsView, initialIndex: Int ->
|
|
41
|
+
view.props.initialIndex = initialIndex
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
Prop("
|
|
44
|
+
Prop("canDismiss") { view: RNToolsSheetsView, canDismiss: Boolean ->
|
|
45
|
+
view.props.canDismiss = canDismiss
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Prop("snapPoints") { view: RNToolsSheetsView, snapPoints: List<Double> ->
|
|
45
49
|
view.props.snapPoints = snapPoints.map { view.convertToPx(it) }
|
|
46
50
|
}
|
|
47
51
|
|
|
@@ -13,10 +13,12 @@ import expo.modules.kotlin.AppContext
|
|
|
13
13
|
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
14
14
|
import expo.modules.kotlin.views.ExpoView
|
|
15
15
|
import android.graphics.Color
|
|
16
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
16
17
|
|
|
17
18
|
class RNToolsSheetsView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
|
|
18
19
|
val onDismiss by EventDispatcher()
|
|
19
20
|
val onStateChange by EventDispatcher()
|
|
21
|
+
val onDismissPrevented by EventDispatcher()
|
|
20
22
|
|
|
21
23
|
var rootViewGroup = SheetRootView(context, appContext)
|
|
22
24
|
private var composeView: ComposeView
|
|
@@ -42,6 +44,15 @@ class RNToolsSheetsView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
42
44
|
hideSheet()
|
|
43
45
|
}
|
|
44
46
|
}
|
|
47
|
+
|
|
48
|
+
LaunchedEffect(props.canDismiss) {
|
|
49
|
+
UiThreadUtil.runOnUiThread {
|
|
50
|
+
bottomSheetDialog?.apply {
|
|
51
|
+
setCancelable(true)
|
|
52
|
+
behavior.isHideable = props.canDismiss
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
45
56
|
}
|
|
46
57
|
}
|
|
47
58
|
|
|
@@ -54,95 +65,116 @@ class RNToolsSheetsView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
private fun hideSheet() {
|
|
57
|
-
|
|
58
|
-
|
|
68
|
+
UiThreadUtil.runOnUiThread {
|
|
69
|
+
bottomSheetDialog?.dismiss()
|
|
70
|
+
bottomSheetDialog = null
|
|
71
|
+
}
|
|
59
72
|
}
|
|
60
73
|
|
|
61
74
|
private fun showSheet() {
|
|
62
|
-
|
|
75
|
+
UiThreadUtil.runOnUiThread {
|
|
76
|
+
(rootViewGroup.parent as? ViewGroup)?.removeView(rootViewGroup)
|
|
63
77
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
78
|
+
val frameLayout = FrameLayout(context).apply {
|
|
79
|
+
layoutParams = LayoutParams(
|
|
80
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
81
|
+
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
82
|
+
)
|
|
69
83
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
val snapPoints = props.snapPoints
|
|
74
|
-
val initialIndex = props.openToIndex
|
|
75
|
-
|
|
76
|
-
val hasTwoSnapPoints = snapPoints.size >= 2
|
|
77
|
-
val peekHeight = if (hasTwoSnapPoints) snapPoints[0] else -1
|
|
78
|
-
val expandedHeight = if (snapPoints.isNotEmpty()) snapPoints.getOrNull(1) ?: snapPoints[0] else -1
|
|
79
|
-
val initialHeight = snapPoints.getOrNull(initialIndex) ?: peekHeight
|
|
84
|
+
addView(rootViewGroup)
|
|
85
|
+
}
|
|
80
86
|
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
val snapPoints = props.snapPoints
|
|
88
|
+
val initialIndex = props.initialIndex
|
|
89
|
+
|
|
90
|
+
val hasTwoSnapPoints = snapPoints.size >= 2
|
|
91
|
+
val peekHeight = if (hasTwoSnapPoints) snapPoints[0] else -1
|
|
92
|
+
val expandedHeight = if (snapPoints.isNotEmpty()) snapPoints.getOrNull(1) ?: snapPoints[0] else -1
|
|
93
|
+
val initialHeight = snapPoints.getOrNull(initialIndex) ?: peekHeight
|
|
94
|
+
|
|
95
|
+
bottomSheetDialog = PreventDismissBottomSheetDialog(
|
|
96
|
+
context = context,
|
|
97
|
+
canDismiss = { props.canDismiss },
|
|
98
|
+
onDismissPrevented = { onDismissPrevented(mapOf()) }
|
|
99
|
+
).apply {
|
|
100
|
+
setCancelable(true)
|
|
101
|
+
setContentView(frameLayout)
|
|
102
|
+
|
|
103
|
+
window?.setDimAmount(props.dimAmount)
|
|
104
|
+
|
|
105
|
+
behavior.isHideable = props.canDismiss
|
|
106
|
+
|
|
107
|
+
window?.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)?.let { bottomSheet ->
|
|
108
|
+
val backgroundColor = props.backgroundColor?.let {
|
|
109
|
+
try {
|
|
110
|
+
Color.parseColor(it)
|
|
111
|
+
} catch (e: IllegalArgumentException) {
|
|
112
|
+
Color.WHITE
|
|
113
|
+
}
|
|
114
|
+
} ?: Color.WHITE
|
|
115
|
+
|
|
116
|
+
val radius = props.cornerRadius ?: 32f
|
|
117
|
+
|
|
118
|
+
val drawable = GradientDrawable().apply {
|
|
119
|
+
setColor(backgroundColor)
|
|
120
|
+
cornerRadii = floatArrayOf(
|
|
121
|
+
radius, radius,
|
|
122
|
+
radius, radius,
|
|
123
|
+
0f, 0f,
|
|
124
|
+
0f, 0f
|
|
125
|
+
)
|
|
126
|
+
}
|
|
83
127
|
|
|
84
|
-
|
|
128
|
+
bottomSheet.background = drawable
|
|
129
|
+
}
|
|
85
130
|
|
|
86
|
-
|
|
131
|
+
val behavior = behavior
|
|
87
132
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
133
|
+
setOnDismissListener {
|
|
134
|
+
onDismiss(mapOf())
|
|
135
|
+
if (behavior.state != BottomSheetBehavior.STATE_HIDDEN) {
|
|
136
|
+
onStateChange(mapOf(
|
|
137
|
+
"type" to "HIDDEN",
|
|
138
|
+
))
|
|
93
139
|
}
|
|
94
|
-
} ?: Color.WHITE
|
|
95
|
-
|
|
96
|
-
val drawable = GradientDrawable().apply {
|
|
97
|
-
setColor(backgroundColor)
|
|
98
|
-
cornerRadius = props.cornerRadius ?: 0f
|
|
99
140
|
}
|
|
100
141
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
val behavior = behavior
|
|
142
|
+
if (peekHeight > 0) {
|
|
143
|
+
behavior.peekHeight = peekHeight
|
|
144
|
+
}
|
|
105
145
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
onStateChange(mapOf(
|
|
110
|
-
"type" to "HIDDEN",
|
|
111
|
-
))
|
|
146
|
+
if (expandedHeight > 0) {
|
|
147
|
+
frameLayout.layoutParams.height = expandedHeight
|
|
148
|
+
frameLayout.requestLayout()
|
|
112
149
|
}
|
|
113
|
-
}
|
|
114
150
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
151
|
+
behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
|
|
152
|
+
override fun onStateChanged(bottomSheet: android.view.View, newState: Int) {
|
|
153
|
+
if (!props.canDismiss && newState == BottomSheetBehavior.STATE_HIDDEN) {
|
|
154
|
+
onDismissPrevented(mapOf())
|
|
155
|
+
}
|
|
118
156
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
frameLayout.requestLayout()
|
|
122
|
-
}
|
|
157
|
+
handleSheetStateChange(newState)
|
|
158
|
+
}
|
|
123
159
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
160
|
+
override fun onSlide(bottomSheet: android.view.View, slideOffset: Float) {
|
|
161
|
+
}
|
|
162
|
+
})
|
|
128
163
|
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
})
|
|
164
|
+
show()
|
|
132
165
|
|
|
133
|
-
|
|
166
|
+
if (initialHeight == peekHeight) {
|
|
167
|
+
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
|
168
|
+
} else {
|
|
169
|
+
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
|
170
|
+
}
|
|
134
171
|
|
|
135
|
-
|
|
136
|
-
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
|
137
|
-
} else {
|
|
138
|
-
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
|
172
|
+
handleSheetStateChange(behavior.state)
|
|
139
173
|
}
|
|
140
|
-
|
|
141
|
-
handleSheetStateChange(behavior.state)
|
|
142
174
|
}
|
|
143
175
|
}
|
|
144
176
|
|
|
145
|
-
fun convertToPx(height:
|
|
177
|
+
fun convertToPx(height: Double): Int {
|
|
146
178
|
val density = context.resources.displayMetrics.density
|
|
147
179
|
return (height * density).toInt()
|
|
148
180
|
}
|
|
@@ -155,12 +187,6 @@ class RNToolsSheetsView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
155
187
|
))
|
|
156
188
|
|
|
157
189
|
}
|
|
158
|
-
BottomSheetBehavior.STATE_SETTLING -> {
|
|
159
|
-
onStateChange(mapOf(
|
|
160
|
-
"type" to "SETTLING",
|
|
161
|
-
))
|
|
162
|
-
}
|
|
163
|
-
|
|
164
190
|
BottomSheetBehavior.STATE_COLLAPSED -> {
|
|
165
191
|
onStateChange(mapOf(
|
|
166
192
|
"type" to "OPEN",
|
|
@@ -188,12 +214,20 @@ class RNToolsSheetsView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
188
214
|
))
|
|
189
215
|
}
|
|
190
216
|
|
|
191
|
-
BottomSheetBehavior.STATE_DRAGGING -> {
|
|
192
|
-
onStateChange(mapOf(
|
|
193
|
-
"type" to "DRAGGING",
|
|
194
|
-
))
|
|
195
|
-
}
|
|
196
217
|
}
|
|
197
218
|
}
|
|
198
219
|
}
|
|
199
220
|
|
|
221
|
+
class PreventDismissBottomSheetDialog(
|
|
222
|
+
context: Context,
|
|
223
|
+
private val canDismiss: () -> Boolean,
|
|
224
|
+
private val onDismissPrevented: () -> Unit
|
|
225
|
+
) : BottomSheetDialog(context) {
|
|
226
|
+
override fun cancel() {
|
|
227
|
+
if (canDismiss()) super.cancel() else onDismissPrevented()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
override fun onBackPressed() {
|
|
231
|
+
if (canDismiss()) super.onBackPressed() else onDismissPrevented()
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -9,9 +9,10 @@ import expo.modules.kotlin.records.Record
|
|
|
9
9
|
|
|
10
10
|
class SheetProps {
|
|
11
11
|
var isOpen by mutableStateOf(false)
|
|
12
|
-
var
|
|
12
|
+
var initialIndex by mutableIntStateOf(0)
|
|
13
13
|
var snapPoints by mutableStateOf<List<Int>>(emptyList())
|
|
14
14
|
lateinit var rootViewGroup: SheetRootView
|
|
15
|
+
var canDismiss by mutableStateOf(true)
|
|
15
16
|
|
|
16
17
|
// Appearance props
|
|
17
18
|
var dimAmount by mutableStateOf(0.56f)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'json'
|
|
2
2
|
|
|
3
|
+
ENV['RCT_NEW_ARCH_ENABLED'] ||= '1'
|
|
3
4
|
package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
|
|
4
5
|
new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
|
|
5
6
|
|
|
@@ -21,12 +22,11 @@ Pod::Spec.new do |s|
|
|
|
21
22
|
s.static_framework = true
|
|
22
23
|
|
|
23
24
|
s.dependency 'ExpoModulesCore'
|
|
24
|
-
s.public_header_files = 'Sources/
|
|
25
|
-
|
|
25
|
+
s.public_header_files = 'Sources/RNToolsTouchHandlerHelper.h'
|
|
26
26
|
# Swift/Objective-C compatibility
|
|
27
27
|
s.pod_target_xcconfig = {
|
|
28
28
|
'DEFINES_MODULE' => 'YES',
|
|
29
|
-
'OTHER_SWIFT_FLAGS' =>
|
|
29
|
+
'OTHER_SWIFT_FLAGS' => '$(inherited)'
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
|
|
@@ -1,28 +1,45 @@
|
|
|
1
1
|
import ExpoModulesCore
|
|
2
2
|
|
|
3
|
+
struct SheetAppearance: Record {
|
|
4
|
+
@Field
|
|
5
|
+
var grabberVisible: Bool?
|
|
6
|
+
|
|
7
|
+
@Field
|
|
8
|
+
var backgroundColor: String?
|
|
9
|
+
|
|
10
|
+
@Field
|
|
11
|
+
var cornerRadius: Float?
|
|
12
|
+
}
|
|
13
|
+
|
|
3
14
|
public class RNToolsSheetsModule: Module {
|
|
4
15
|
public func definition() -> ModuleDefinition {
|
|
5
16
|
Name("RNToolsSheets")
|
|
6
17
|
|
|
7
18
|
View(RNToolsSheetsView.self) {
|
|
8
|
-
Events("onDismiss", "onStateChange")
|
|
19
|
+
Events("onDismiss", "onStateChange", "onDismissPrevented")
|
|
9
20
|
|
|
10
|
-
Prop("snapPoints") { (view, snapPoints: [
|
|
11
|
-
view.
|
|
21
|
+
Prop("snapPoints") { (view, snapPoints: [CGFloat]) in
|
|
22
|
+
view.updateSnapPoints(snapPoints)
|
|
12
23
|
}
|
|
13
24
|
|
|
14
25
|
Prop("isOpen") { (view, isOpen: Bool) in
|
|
15
|
-
view.
|
|
26
|
+
view.updateIsOpen(isOpen)
|
|
16
27
|
}
|
|
17
28
|
|
|
18
|
-
Prop("
|
|
19
|
-
view.
|
|
29
|
+
Prop("initialIndex") { (view, initialIndex: Int) in
|
|
30
|
+
view.updateInitialIndex(initialIndex)
|
|
20
31
|
}
|
|
21
|
-
|
|
32
|
+
|
|
22
33
|
Prop("appearanceIOS") { (view, appearance: SheetAppearance) in
|
|
23
|
-
view.
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
view.updateAppearance(
|
|
35
|
+
grabberVisible: appearance.grabberVisible ?? true,
|
|
36
|
+
backgroundColor: appearance.backgroundColor,
|
|
37
|
+
cornerRadius: appearance.cornerRadius
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Prop("canDismiss") { (view, canDismiss: Bool) in
|
|
42
|
+
view.updateCanDismiss(canDismiss)
|
|
26
43
|
}
|
|
27
44
|
}
|
|
28
45
|
}
|