react-native-nitro-modal 0.1.5 → 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 +65 -11
- 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 +39 -7
- package/lib/module/NitroModal.js.map +1 -1
- package/lib/module/NitroModalView.js +4 -3
- package/lib/module/NitroModalView.js.map +1 -1
- package/lib/module/NitroModalView.native.js +3 -0
- package/lib/module/NitroModalView.native.js.map +1 -1
- package/lib/module/NitroModalView.web.js +159 -0
- package/lib/module/NitroModalView.web.js.map +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/web/AnimatedValue.js +126 -0
- package/lib/module/web/AnimatedValue.js.map +1 -0
- package/lib/module/web/ModalConfig.js +30 -0
- package/lib/module/web/ModalConfig.js.map +1 -0
- package/lib/module/web/ModalController.js +306 -0
- package/lib/module/web/ModalController.js.map +1 -0
- package/lib/module/web/ModalPresenter.js +280 -0
- package/lib/module/web/ModalPresenter.js.map +1 -0
- package/lib/module/web/PopupPresenter.js +117 -0
- package/lib/module/web/PopupPresenter.js.map +1 -0
- package/lib/module/web/SheetPresenter.js +494 -0
- package/lib/module/web/SheetPresenter.js.map +1 -0
- package/lib/module/web/dom.js +138 -0
- package/lib/module/web/dom.js.map +1 -0
- package/lib/typescript/src/NitroModal.d.ts +35 -4
- 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.d.ts +2 -1
- package/lib/typescript/src/NitroModalView.d.ts.map +1 -1
- package/lib/typescript/src/NitroModalView.native.d.ts +1 -0
- package/lib/typescript/src/NitroModalView.native.d.ts.map +1 -1
- package/lib/typescript/src/NitroModalView.web.d.ts +6 -0
- package/lib/typescript/src/NitroModalView.web.d.ts.map +1 -0
- 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 +49 -0
- package/lib/typescript/src/web/AnimatedValue.d.ts.map +1 -0
- package/lib/typescript/src/web/ModalConfig.d.ts +30 -0
- package/lib/typescript/src/web/ModalConfig.d.ts.map +1 -0
- package/lib/typescript/src/web/ModalController.d.ts +70 -0
- package/lib/typescript/src/web/ModalController.d.ts.map +1 -0
- package/lib/typescript/src/web/ModalPresenter.d.ts +107 -0
- package/lib/typescript/src/web/ModalPresenter.d.ts.map +1 -0
- package/lib/typescript/src/web/PopupPresenter.d.ts +20 -0
- package/lib/typescript/src/web/PopupPresenter.d.ts.map +1 -0
- package/lib/typescript/src/web/SheetPresenter.d.ts +65 -0
- package/lib/typescript/src/web/SheetPresenter.d.ts.map +1 -0
- package/lib/typescript/src/web/dom.d.ts +42 -0
- package/lib/typescript/src/web/dom.d.ts.map +1 -0
- 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 +3 -2
- package/src/NitroModal.nitro.ts +30 -2
- package/src/NitroModal.tsx +71 -8
- package/src/NitroModalView.native.tsx +4 -0
- package/src/NitroModalView.tsx +9 -4
- package/src/NitroModalView.web.tsx +180 -0
- package/src/index.tsx +2 -0
- package/src/web/AnimatedValue.ts +193 -0
- package/src/web/ModalConfig.ts +67 -0
- package/src/web/ModalController.ts +338 -0
- package/src/web/ModalPresenter.ts +333 -0
- package/src/web/PopupPresenter.ts +129 -0
- package/src/web/SheetPresenter.ts +647 -0
- package/src/web/dom.ts +171 -0
package/README.md
CHANGED
|
@@ -7,17 +7,19 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/react-native-nitro-modal)
|
|
8
8
|
[](https://www.npmjs.com/package/react-native-nitro-modal)
|
|
9
9
|
[](LICENSE)
|
|
10
|
-

|
|
10
|
+

|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
`react-native-nitro-modal` renders your React content inside native modal presentations — an edge-attached UIKit sheet on iOS and Material `BottomSheetBehavior` on Android — so gestures, detent snapping, keyboard handling and transitions are handled natively, not re-implemented in JavaScript.
|
|
16
|
+
`react-native-nitro-modal` renders your React content inside native modal presentations — an edge-attached UIKit sheet on iOS and Material `BottomSheetBehavior` on Android — so gestures, detent snapping, keyboard handling and transitions are handled natively, not re-implemented in JavaScript. On the web (React Native Web), the same API renders a DOM sheet and popup with matching behavior.
|
|
17
17
|
|
|
18
18
|
## Features
|
|
19
19
|
|
|
20
|
-
- 📄 **Native bottom sheets** with multiple detents (`small`, `medium`, `large`, `fitContent`) and swipe-to-dismiss
|
|
20
|
+
- 📄 **Native bottom sheets** with multiple detents (`small`, `medium`, `large`, `fitContent` or a height) and swipe-to-dismiss
|
|
21
|
+
- 📌 **Inline sheets** — a persistent sheet that is part of your screen, with the views behind it still usable
|
|
22
|
+
- 🔄 **Pull to refresh** — pulling a sheet down past its lowest detent
|
|
21
23
|
- 🪟 **Centered popups** with native `scale` / `fade` transitions
|
|
22
24
|
- 📏 **Content-sized sheets** — `fitContent` measures your React content and grows with it
|
|
23
25
|
- ⌨️ **Keyboard aware** — the sheet or card follows the keyboard animation (`pan` or `resize`)
|
|
@@ -35,9 +37,10 @@
|
|
|
35
37
|
| `react-native-nitro-modules` | `^0.37.1` |
|
|
36
38
|
| iOS | 15.0+ (16.0+ for `small` and `fitContent`) |
|
|
37
39
|
| Android | API 24 (Android 7.0)+ |
|
|
40
|
+
| Web | via `react-native-web` (tested with 0.21) |
|
|
38
41
|
|
|
39
42
|
> [!NOTE]
|
|
40
|
-
>
|
|
43
|
+
> Other platforms (e.g. macOS, Windows) are not supported. There the component renders nothing and logs a one-time warning.
|
|
41
44
|
|
|
42
45
|
## Installation
|
|
43
46
|
|
|
@@ -110,6 +113,38 @@ Detents are listed smallest first. `initialDetentIndex` selects where the sheet
|
|
|
110
113
|
</NitroModal>
|
|
111
114
|
```
|
|
112
115
|
|
|
116
|
+
### Inline (persistent) bottom sheet
|
|
117
|
+
|
|
118
|
+
`presentation="inline"` keeps the sheet inside the component's own frame instead of presenting it over the app — like a sheet that is part of the screen. There is no backdrop, the views behind it stay interactive, it moves with your screen during navigation, and the user can't dismiss it. Position the frame with `style` (it fills its parent by default); the sheet rests against its bottom edge and its largest detent reaches its top edge.
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<View style={{ flex: 1 }}>
|
|
122
|
+
<ScreenContent />
|
|
123
|
+
|
|
124
|
+
<NitroModal
|
|
125
|
+
isOpen
|
|
126
|
+
presentation="inline"
|
|
127
|
+
style={{ top: headerHeight }}
|
|
128
|
+
detents={[320, 'large']}
|
|
129
|
+
onPullToRefresh={refresh}
|
|
130
|
+
>
|
|
131
|
+
<FlatList data={rows} renderItem={renderRow} nestedScrollEnabled />
|
|
132
|
+
</NitroModal>
|
|
133
|
+
</View>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
A number in `detents` is the content height in dp/pt above the bottom safe area.
|
|
137
|
+
|
|
138
|
+
### Scrolling content and pull to refresh
|
|
139
|
+
|
|
140
|
+
A sheet hands drags off to the scroll view under the finger:
|
|
141
|
+
|
|
142
|
+
- Dragging up expands the sheet first, then scrolls the content.
|
|
143
|
+
- Dragging down scrolls the content back to its top. Once the content has scrolled during a drag, that drag never moves the sheet; the next drag collapses it.
|
|
144
|
+
- With `onPullToRefresh` set, a drag that starts with the sheet resting on its lowest detent can pull it further down. Letting go past the threshold calls `onPullToRefresh` (with haptic feedback when it is crossed). This needs a sheet that can't be swiped away: inline, or `dismissOnSwipe={false}`.
|
|
145
|
+
|
|
146
|
+
On Android, scrollable content takes part through nested scrolling, so set `nestedScrollEnabled` on it.
|
|
147
|
+
|
|
113
148
|
### Popup
|
|
114
149
|
|
|
115
150
|
```tsx
|
|
@@ -167,14 +202,16 @@ const sheet = useRef<NitroModalRef>(null);
|
|
|
167
202
|
| ------------------------ | ---------------------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------- |
|
|
168
203
|
| `isOpen` | `boolean` | — | Controls visibility. Leave undefined to use the [ref API](#nitromodalref) instead. |
|
|
169
204
|
| `mode` | `'bottomSheet' \| 'popup'` | `'bottomSheet'` | How the modal is presented. |
|
|
170
|
-
| `
|
|
205
|
+
| `presentation` | `'modal' \| 'inline'` | `'modal'` | `inline` keeps a bottom sheet inside the component's frame, as part of the screen. See [Inline](#inline-persistent-bottom-sheet). |
|
|
206
|
+
| `style` | `StyleProp<ViewStyle>` | `absoluteFill` | Inline only: the frame the sheet lives in. |
|
|
207
|
+
| `detents` | `SheetDetent[]` | `['fitContent']` | Sheet heights, smallest first: named sizes or a content height in dp/pt. Android uses at most three. |
|
|
171
208
|
| `initialDetentIndex` | `number` | `0` | Index into `detents` the sheet opens at. |
|
|
172
209
|
| `backdropColor` | `ColorValue` | `'black'` | Backdrop color. |
|
|
173
210
|
| `backdropOpacity` | `number` | `0.4` | Backdrop opacity, `0`–`1`. |
|
|
174
|
-
| `backdropBlur` | `number` | `0` | Blur radius (dp/pt) behind the modal. `0` disables it. See [platform notes](#platform-notes).
|
|
211
|
+
| `backdropBlur` | `number` | `0` | Blur radius (dp/pt/px) behind the modal. `0` disables it. See [platform notes](#platform-notes). |
|
|
175
212
|
| `dismissOnBackdropPress` | `boolean` | `true` | Tapping the backdrop closes the modal. |
|
|
176
213
|
| `dismissOnSwipe` | `boolean` | `true` | Swiping down closes a bottom sheet. |
|
|
177
|
-
| `dismissOnBackButton` | `boolean` | `true` | The Android back button/gesture closes the modal.
|
|
214
|
+
| `dismissOnBackButton` | `boolean` | `true` | The Android back button/gesture (Escape on web) closes the modal. |
|
|
178
215
|
| `showGrabber` | `boolean` | `false` | Shows the drag handle on a bottom sheet. |
|
|
179
216
|
| `cornerRadius` | `number` | platform default | Corner radius of the sheet/card. |
|
|
180
217
|
| `backgroundColor` | `ColorValue` | system surface | Background of the sheet/card. |
|
|
@@ -192,7 +229,8 @@ const sheet = useRef<NitroModalRef>(null);
|
|
|
192
229
|
| `onDismiss` | `(reason: DismissReason) => void` | The modal is fully gone. Fires exactly once per presentation. |
|
|
193
230
|
| `onDetentChange` | `(index: number) => void` | A bottom sheet settled on a different detent. |
|
|
194
231
|
| `onBackdropPress` | `() => void` | The backdrop was tapped (fires even when `dismissOnBackdropPress` is `false`). |
|
|
195
|
-
| `onBackButtonPress` | `() => void` | The Android hardware/gesture back was pressed.
|
|
232
|
+
| `onBackButtonPress` | `() => void` | The Android hardware/gesture back (Escape on web) was pressed. |
|
|
233
|
+
| `onPullToRefresh` | `() => void` | The sheet was pulled down past its lowest detent and released. See [pull to refresh](#scrolling-content-and-pull-to-refresh). |
|
|
196
234
|
|
|
197
235
|
### `NitroModalRef`
|
|
198
236
|
|
|
@@ -207,12 +245,17 @@ const sheet = useRef<NitroModalRef>(null);
|
|
|
207
245
|
```ts
|
|
208
246
|
type ModalMode = 'bottomSheet' | 'popup';
|
|
209
247
|
|
|
210
|
-
type
|
|
248
|
+
type ModalPresentation = 'modal' | 'inline';
|
|
249
|
+
|
|
250
|
+
type NamedSheetDetent =
|
|
211
251
|
| 'small' // ~25% of the available height
|
|
212
252
|
| 'medium' // ~50% of the available height
|
|
213
253
|
| 'large' // the full available height
|
|
214
254
|
| 'fitContent'; // the measured height of your content
|
|
215
255
|
|
|
256
|
+
// A named height, or the content height in dp/pt above the bottom safe area.
|
|
257
|
+
type SheetDetent = NamedSheetDetent | number;
|
|
258
|
+
|
|
216
259
|
type KeyboardBehavior = 'pan' | 'resize' | 'none';
|
|
217
260
|
|
|
218
261
|
type PopupAnimation = 'scale' | 'fade' | 'none';
|
|
@@ -227,6 +270,8 @@ import type {
|
|
|
227
270
|
DismissReason,
|
|
228
271
|
KeyboardBehavior,
|
|
229
272
|
ModalMode,
|
|
273
|
+
ModalPresentation,
|
|
274
|
+
NamedSheetDetent,
|
|
230
275
|
NitroModalProps,
|
|
231
276
|
NitroModalRef,
|
|
232
277
|
PopupAnimation,
|
|
@@ -236,7 +281,7 @@ import type {
|
|
|
236
281
|
|
|
237
282
|
## How it works
|
|
238
283
|
|
|
239
|
-
`<NitroModal>` mounts a zero-size placeholder in your React tree. When opened, the native side presents a real view controller (iOS) or window (Android) and hosts your React children inside it. Children mount when the modal opens and stay mounted until the native exit animation completes, so content never disappears mid-transition. The native side also reports the exact area available to the content (accounting for rotation, keyboard and sheet size), and the content container is sized accordingly.
|
|
284
|
+
`<NitroModal>` mounts a zero-size placeholder in your React tree. When opened, the native side presents a real view controller (iOS) or window (Android) and hosts your React children inside it. An inline sheet instead lives in a view laid out with `style`, in your screen's own hierarchy: touches that miss the sheet go to the views behind it, and `keyboardBehavior` doesn't apply (the screen's own keyboard handling does). Children mount when the modal opens and stay mounted until the native exit animation completes, so content never disappears mid-transition. The native side also reports the exact area available to the content (accounting for rotation, keyboard and sheet size), and the content container is sized accordingly.
|
|
240
285
|
|
|
241
286
|
## Platform notes
|
|
242
287
|
|
|
@@ -253,6 +298,15 @@ import type {
|
|
|
253
298
|
- Predictive back (Android 14+, when the app opts in or targets SDK 36) previews the exit while the gesture runs: the sheet uses Material's bottom-sheet animation and the popup scales down.
|
|
254
299
|
- Transitions are interruptible: closing during the enter animation turns it around, and reopening during the exit animation brings the same modal back (no extra `onDismiss`/`onPresent`). Once the exit starts, taps and back presses reach the screen below, as with a native dialog.
|
|
255
300
|
|
|
301
|
+
**Web**
|
|
302
|
+
|
|
303
|
+
- Renders through React Native Web's `Modal`, so it stacks with your other modals, traps focus while open and returns it on close. Nothing is rendered in place.
|
|
304
|
+
- Bottom sheets can be dragged with touch or mouse, with the same detent snapping, fling-to-dismiss and hand-off to scrollable content as on iOS. A touch that starts on content already scrolled down scrolls it natively instead of moving the sheet.
|
|
305
|
+
- The Escape key acts as the back button: it fires `onBackButtonPress` and, unless `dismissOnBackButton` is `false`, dismisses the topmost modal with reason `'backButton'`. The browser's history back is not intercepted.
|
|
306
|
+
- `backdropBlur` uses CSS `backdrop-filter`. Without `backgroundColor`, the sheet/card uses the CSS `Canvas` system color, which follows your page's `color-scheme`.
|
|
307
|
+
- `keyboardBehavior` follows the on-screen keyboard through the `visualViewport` API (mobile browsers). Safe-area insets are respected when the page uses `viewport-fit=cover`.
|
|
308
|
+
- Sheets are at most 640px wide and centered. Animations are skipped when the user prefers reduced motion.
|
|
309
|
+
|
|
256
310
|
**Colors**
|
|
257
311
|
|
|
258
312
|
- `backdropColor` and `backgroundColor` accept any color string or number supported by `processColor`. `PlatformColor` and `DynamicColorIOS` values are not supported yet.
|
|
@@ -263,7 +317,7 @@ The repository includes an example app that covers content-sized sheets, multi-d
|
|
|
263
317
|
|
|
264
318
|
```sh
|
|
265
319
|
yarn
|
|
266
|
-
yarn example ios # or: yarn example android
|
|
320
|
+
yarn example ios # or: yarn example android / yarn example web
|
|
267
321
|
```
|
|
268
322
|
|
|
269
323
|
## Contributing
|