Skip to content

Commit 22e4879

Browse files
authored
Feat: Named sheet (#6)
* refactor: tidy * feat: add `name` prop + static methods
1 parent 04577d3 commit 22e4879

File tree

10 files changed

+249
-51
lines changed

10 files changed

+249
-51
lines changed

README.md

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The true native bottom sheet 💩
1616
* ✅ Handles your _Footer_ needs, natively.
1717
* ✅ Handles your _Keyboard_ needs, natively.
1818
* ✅ Asynchronus `ref` [methods](#methods).
19-
* ✅ Bonus! [Blur](#blurtint) support on iOS 😎
19+
* ✅ Bonus! [Blur](#blurtint) support on IOS 😎
2020

2121
## Installation
2222

@@ -28,8 +28,7 @@ yarn add @lodev09/react-native-true-sheet
2828
npm i @lodev09/react-native-true-sheet
2929
```
3030

31-
> [!NOTE]
32-
> This package is not compatible with [Expo Go](https://docs.expo.dev/get-started/expo-go/). However, it does work with Expo [`prebuild`](https://docs.expo.dev/workflow/prebuild/).
31+
> This package is not compatible with [Expo Go](https://docs.expo.dev/get-started/expo-go/). Use this with [Expo CNG](https://docs.expo.dev/workflow/continuous-native-generation/) instead.
3332
3433
## Usage
3534

@@ -67,6 +66,7 @@ Extends `ViewProps`
6766
| Prop | Type | Default | Description | 🍎 | 🤖 |
6867
| - | - | - | - | - | - |
6968
| sizes | [`SheetSize[]`](#sheetsize) | `["medium", "large"]` | Array of sizes you want the sheet to support. Maximum of _**3 sizes**_ only! **_collapsed_**, **_half-expanded_**, and **_expanded_**. Example: `size={["auto", "60%", "large"]}`|||
69+
| name | `string` | - | The name to reference this sheet. It has to be **_unique_**. You can then present this sheet globally using its name. |||
7070
| backgroundColor | `ColorValue` | `"white"` | The sheet's background color. |||
7171
| cornerRadius | `number` | - | the sheet corner radius. |||
7272
| maxHeight | `number` | - | Overrides `"large"` or `"100%"` height. |||
@@ -75,8 +75,8 @@ Extends `ViewProps`
7575
| dismissible | `boolean` | `true` | If set to `false`, the sheet will prevent interactive dismissal via dragging or clicking outside of it. |||
7676
| grabber | `boolean` | `true` | Shows a grabber (or handle). Native on IOS and styled `View` on Android. |||
7777
| grabberProps | [`TrueSheetGrabberProps`](#truesheetgrabberprops) | - | Overrides the grabber props for android. | ||
78-
| blurTint | [`BlurTint`](#blurtint) | - | The blur effect style on iOS. Overrides `backgroundColor` if set. Example: `"light"`, `"dark"`, etc. || |
79-
| scrollRef | `RefObject<...>` | - | The main scrollable ref that the sheet should handle on iOS. || |
78+
| blurTint | [`BlurTint`](#blurtint) | - | The blur effect style on IOS. Overrides `backgroundColor` if set. Example: `"light"`, `"dark"`, etc. || |
79+
| scrollRef | `RefObject<...>` | - | The main scrollable ref that the sheet should handle on IOS. || |
8080

8181
## Methods
8282

@@ -108,6 +108,37 @@ return (
108108
| resize | `index: number` | Resizes the sheet programmatically by `index`. This is an alias of the `present(index)` method. |
109109
| dismiss | - | Dismisses the sheet. |
110110

111+
### Static Methods
112+
113+
You can also call the above methods statically without having access to a sheet's `ref`. This is particularly useful when you want to present a sheet from anywhere.
114+
115+
The API is similar to the ref methods except for the required `name` prop.
116+
117+
```ts
118+
TrueSheet.present('SHEET-NAME')
119+
TrueSheet.dismiss('SHEET-NAME')
120+
TrueSheet.resize('SHEET-NAME', 1)
121+
```
122+
123+
Example:
124+
```tsx
125+
// Define the sheet as usual
126+
<TrueSheet name="my-sheet">
127+
// ...
128+
</TrueSheet>
129+
```
130+
```tsx
131+
// Somewhere in your screen
132+
const presentMySheet = async () => {
133+
await TrueSheet.present('my-sheet')
134+
console.log('🎉')
135+
}
136+
137+
return (
138+
<Button onPress={presentMySheet} />
139+
)
140+
```
141+
111142
## Events
112143

113144
```tsx
@@ -166,7 +197,7 @@ Grabber props to be used for android grabber or handle.
166197

167198
### `BlurTint`
168199

169-
Blur tint that is mapped into native values in iOS.
200+
Blur tint that is mapped into native values in IOS.
170201

171202
```tsx
172203
<TrueSheet blurTint="dark">
@@ -224,6 +255,55 @@ jest.mock('@lodev09/react-native-true-sheet')
224255

225256
## Troubleshooting
226257

258+
### Presenting a sheet on top of an existing sheet on **IOS**
259+
260+
On IOS, presenting a sheet on top of an existing sheet will cause error.
261+
262+
```console
263+
Attempt to present <TrueSheet.TrueSheetViewController: 0x11829f800> on <UIViewController: 0x10900eb10> (from <RNSScreen: 0x117dbf400>) which is already presenting <TrueSheet.TrueSheetViewController: 0x11a0b9200>
264+
```
265+
266+
There are _two_ ways to resolve this.
267+
268+
1. Dismiss the "parent" sheet first
269+
```tsx
270+
const presentSheet2 = async () => {
271+
await sheet1.current?.dismiss() // Wait for sheet 1 to dismiss ✅
272+
await sheet2.current?.present() // Sheet 2 will now present 🎉
273+
}
274+
275+
return (
276+
<>
277+
<TrueSheet ref={sheet1}>
278+
<Button onPress={presentSheet2} title="Present Sheet 2" />
279+
// ...
280+
</TrueSheet>
281+
282+
<TrueSheet ref={sheet2}>
283+
// ...
284+
</TrueSheet>
285+
</>
286+
)
287+
```
288+
2. Define the 2nd sheet within the "parent" sheet. IOS handles this automatically by default 😎.
289+
```tsx
290+
const presentSheet2 = async () => {
291+
await sheet2.current?.present() // Sheet 2 will now present 🎉
292+
}
293+
294+
return (
295+
<TrueSheet ref={sheet1}>
296+
<Button onPress={presentSheet2} title="Present Sheet 2" />
297+
298+
// ...
299+
300+
<TrueSheet ref={sheet2}>
301+
// ...
302+
</TrueSheet>
303+
</TrueSheet>
304+
)
305+
```
306+
227307
### Handling `ScrollView` on **Android**
228308

229309
On Android, `nestedScrollEnabled` needs to be enabled so that scrolling works when the sheet is expanded to its `maxHeight`.
@@ -254,9 +334,9 @@ return (
254334
)
255335
```
256336

257-
### Integrating `@react-navigation/native` on iOS
337+
### Integrating `@react-navigation/native` on **IOS**
258338

259-
On iOS, navigating to a [React Navigation](https://reactnavigation.org) screen from within the Sheet can cause issues. To resolve this, dismiss the sheet before navigating!
339+
On IOS, navigating to a [React Navigation](https://reactnavigation.org) screen from within the Sheet can cause issues. To resolve this, dismiss the sheet before navigating!
260340

261341
Example:
262342
```tsx
@@ -283,7 +363,6 @@ The sheet does not have control over how React Native renders components and may
283363

284364
- [ ] Inline sheet
285365
- [ ] Test with RN new architecture
286-
- [ ] Context/Hooks
287366
- [ ] Reanimated integration(?)
288367

289368
## Contributing

example/src/components/Spacer.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react'
2+
import { View, type ViewProps } from 'react-native'
3+
4+
import { SPACING } from '../utils'
5+
6+
interface SpacerProps extends ViewProps {
7+
space?: number
8+
}
9+
10+
export const Spacer = (props: SpacerProps) => {
11+
const { space = SPACING, style: $styleOverride, ...rest } = props
12+
return <View style={[{ height: space }, $styleOverride]} {...rest} />
13+
}

example/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './Footer'
22
export * from './DemoContent'
33
export * from './Button'
4+
export * from './Spacer'

example/src/sheets/BasicSheet.tsx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import { type ViewStyle } from 'react-native'
33
import { TrueSheet, type TrueSheetProps } from '@lodev09/react-native-true-sheet'
44

55
import { DARK, DARK_BLUE, GRABBER_COLOR, RANDOM_TEXTS, SPACING, random } from '../utils'
6-
import { Button, DemoContent, Footer } from '../components'
6+
import { Button, DemoContent, Footer, Spacer } from '../components'
77

88
interface BasicSheetProps extends TrueSheetProps {}
99

1010
export const BasicSheet = forwardRef((props: BasicSheetProps, ref: Ref<TrueSheet>) => {
1111
const sheetRef = useRef<TrueSheet>(null)
12+
const childSheet = useRef<TrueSheet>(null)
1213

1314
const resize = async (index: number) => {
1415
await sheetRef.current?.resize(index)
@@ -20,6 +21,20 @@ export const BasicSheet = forwardRef((props: BasicSheetProps, ref: Ref<TrueSheet
2021
console.log('Basic sheet dismiss asynced')
2122
}
2223

24+
const presentChild = async () => {
25+
// Note: no need to dismiss this sheet 😎
26+
await childSheet.current?.present()
27+
28+
console.log('Child sheet presented!')
29+
}
30+
31+
const presentPromptSheet = async () => {
32+
// Note: we need to dismiss this sheet first
33+
await sheetRef.current?.dismiss()
34+
35+
await TrueSheet.present('prompt-sheet')
36+
}
37+
2338
useImperativeHandle<TrueSheet | null, TrueSheet | null>(ref, () => sheetRef.current)
2439

2540
return (
@@ -43,7 +58,25 @@ export const BasicSheet = forwardRef((props: BasicSheetProps, ref: Ref<TrueSheet
4358
<Button text="Present Large" onPress={() => resize(2)} />
4459
<Button text="Present 80%" onPress={() => resize(1)} />
4560
<Button text="Present Auto" onPress={() => resize(0)} />
46-
<Button text="Dismis" onPress={dismiss} />
61+
<Spacer />
62+
<Button text="Present Child Sheet" onPress={presentChild} />
63+
<Button text="Present PromptSheet" onPress={presentPromptSheet} />
64+
<Spacer />
65+
<Spacer />
66+
<Button text="Dismiss" onPress={dismiss} />
67+
68+
<TrueSheet
69+
ref={childSheet}
70+
sizes={['auto']}
71+
backgroundColor={DARK}
72+
contentContainerStyle={$content}
73+
FooterComponent={<Footer />}
74+
>
75+
<DemoContent color={DARK_BLUE} text={random(RANDOM_TEXTS)} />
76+
<DemoContent color={DARK_BLUE} text={random(RANDOM_TEXTS)} />
77+
<DemoContent color={DARK_BLUE} text={random(RANDOM_TEXTS)} />
78+
<Button text="Close" onPress={() => childSheet.current?.dismiss()} />
79+
</TrueSheet>
4780
</TrueSheet>
4881
)
4982
})

example/src/sheets/PromptSheet.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export const PromptSheet = forwardRef((props: PromptSheetProps, ref: Ref<TrueShe
3030
return (
3131
<TrueSheet
3232
ref={sheetRef}
33+
name="prompt-sheet"
3334
sizes={['auto', '80%']}
3435
dismissible={false}
3536
contentContainerStyle={$content}

ios/TrueSheetView.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,9 +299,6 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
299299
return
300300
}
301301

302-
// Dismiss the keyboard
303-
contentView?.endEditing(true)
304-
305302
viewController.dismiss(animated: true) {
306303
promise.resolve(nil)
307304
}

0 commit comments

Comments
 (0)