@oxyhq/bloom 0.3.12 → 0.5.0

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.
Files changed (191) hide show
  1. package/README.md +133 -90
  2. package/lib/commonjs/bottom-sheet/index.js +341 -98
  3. package/lib/commonjs/bottom-sheet/index.js.map +1 -1
  4. package/lib/commonjs/context-menu/index.js +18 -19
  5. package/lib/commonjs/context-menu/index.js.map +1 -1
  6. package/lib/commonjs/dialog/BloomDialogProvider.js +61 -0
  7. package/lib/commonjs/dialog/BloomDialogProvider.js.map +1 -0
  8. package/lib/commonjs/dialog/BloomDialogProvider.web.js +45 -0
  9. package/lib/commonjs/dialog/BloomDialogProvider.web.js.map +1 -0
  10. package/lib/commonjs/dialog/Dialog.js +197 -100
  11. package/lib/commonjs/dialog/Dialog.js.map +1 -1
  12. package/lib/commonjs/dialog/Dialog.web.js +194 -84
  13. package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
  14. package/lib/commonjs/dialog/SheetShell.js +149 -0
  15. package/lib/commonjs/dialog/SheetShell.js.map +1 -0
  16. package/lib/commonjs/dialog/alert-store.js +116 -0
  17. package/lib/commonjs/dialog/alert-store.js.map +1 -0
  18. package/lib/commonjs/dialog/alert.js +38 -0
  19. package/lib/commonjs/dialog/alert.js.map +1 -0
  20. package/lib/commonjs/dialog/context.js +10 -2
  21. package/lib/commonjs/dialog/context.js.map +1 -1
  22. package/lib/commonjs/dialog/index.js +8 -24
  23. package/lib/commonjs/dialog/index.js.map +1 -1
  24. package/lib/commonjs/dialog/index.web.js +10 -20
  25. package/lib/commonjs/dialog/index.web.js.map +1 -1
  26. package/lib/commonjs/index.js +101 -66
  27. package/lib/commonjs/index.js.map +1 -1
  28. package/lib/commonjs/index.web.js +101 -66
  29. package/lib/commonjs/index.web.js.map +1 -1
  30. package/lib/commonjs/menu/index.js +21 -23
  31. package/lib/commonjs/menu/index.js.map +1 -1
  32. package/lib/commonjs/select/index.js +26 -27
  33. package/lib/commonjs/select/index.js.map +1 -1
  34. package/lib/commonjs/toast/index.js +42 -13
  35. package/lib/commonjs/toast/index.js.map +1 -1
  36. package/lib/commonjs/toast/index.web.js +19 -15
  37. package/lib/commonjs/toast/index.web.js.map +1 -1
  38. package/lib/module/bottom-sheet/index.js +341 -98
  39. package/lib/module/bottom-sheet/index.js.map +1 -1
  40. package/lib/module/context-menu/index.js +15 -16
  41. package/lib/module/context-menu/index.js.map +1 -1
  42. package/lib/module/dialog/BloomDialogProvider.js +57 -0
  43. package/lib/module/dialog/BloomDialogProvider.js.map +1 -0
  44. package/lib/module/dialog/BloomDialogProvider.web.js +41 -0
  45. package/lib/module/dialog/BloomDialogProvider.web.js.map +1 -0
  46. package/lib/module/dialog/Dialog.js +199 -87
  47. package/lib/module/dialog/Dialog.js.map +1 -1
  48. package/lib/module/dialog/Dialog.web.js +195 -70
  49. package/lib/module/dialog/Dialog.web.js.map +1 -1
  50. package/lib/module/dialog/SheetShell.js +143 -0
  51. package/lib/module/dialog/SheetShell.js.map +1 -0
  52. package/lib/module/dialog/alert-store.js +107 -0
  53. package/lib/module/dialog/alert-store.js.map +1 -0
  54. package/lib/module/dialog/alert.js +35 -0
  55. package/lib/module/dialog/alert.js.map +1 -0
  56. package/lib/module/dialog/context.js +10 -2
  57. package/lib/module/dialog/context.js.map +1 -1
  58. package/lib/module/dialog/index.js +3 -1
  59. package/lib/module/dialog/index.js.map +1 -1
  60. package/lib/module/dialog/index.web.js +9 -7
  61. package/lib/module/dialog/index.web.js.map +1 -1
  62. package/lib/module/index.js +2 -3
  63. package/lib/module/index.js.map +1 -1
  64. package/lib/module/index.web.js +2 -3
  65. package/lib/module/index.web.js.map +1 -1
  66. package/lib/module/menu/index.js +11 -13
  67. package/lib/module/menu/index.js.map +1 -1
  68. package/lib/module/select/index.js +27 -28
  69. package/lib/module/select/index.js.map +1 -1
  70. package/lib/module/toast/index.js +41 -11
  71. package/lib/module/toast/index.js.map +1 -1
  72. package/lib/module/toast/index.web.js +18 -13
  73. package/lib/module/toast/index.web.js.map +1 -1
  74. package/lib/typescript/commonjs/__tests__/Dialog.test.d.ts +2 -0
  75. package/lib/typescript/commonjs/__tests__/Dialog.test.d.ts.map +1 -0
  76. package/lib/typescript/commonjs/bottom-sheet/index.d.ts +47 -1
  77. package/lib/typescript/commonjs/bottom-sheet/index.d.ts.map +1 -1
  78. package/lib/typescript/commonjs/context-menu/index.d.ts +4 -3
  79. package/lib/typescript/commonjs/context-menu/index.d.ts.map +1 -1
  80. package/lib/typescript/commonjs/dialog/BloomDialogProvider.d.ts +27 -0
  81. package/lib/typescript/commonjs/dialog/BloomDialogProvider.d.ts.map +1 -0
  82. package/lib/typescript/commonjs/dialog/BloomDialogProvider.web.d.ts +15 -0
  83. package/lib/typescript/commonjs/dialog/BloomDialogProvider.web.d.ts.map +1 -0
  84. package/lib/typescript/commonjs/dialog/Dialog.d.ts +37 -10
  85. package/lib/typescript/commonjs/dialog/Dialog.d.ts.map +1 -1
  86. package/lib/typescript/commonjs/dialog/Dialog.web.d.ts +26 -10
  87. package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
  88. package/lib/typescript/commonjs/dialog/SheetShell.d.ts +31 -0
  89. package/lib/typescript/commonjs/dialog/SheetShell.d.ts.map +1 -0
  90. package/lib/typescript/commonjs/dialog/alert-store.d.ts +70 -0
  91. package/lib/typescript/commonjs/dialog/alert-store.d.ts.map +1 -0
  92. package/lib/typescript/commonjs/dialog/alert.d.ts +27 -0
  93. package/lib/typescript/commonjs/dialog/alert.d.ts.map +1 -0
  94. package/lib/typescript/commonjs/dialog/context.d.ts +7 -0
  95. package/lib/typescript/commonjs/dialog/context.d.ts.map +1 -1
  96. package/lib/typescript/commonjs/dialog/index.d.ts +5 -2
  97. package/lib/typescript/commonjs/dialog/index.d.ts.map +1 -1
  98. package/lib/typescript/commonjs/dialog/index.web.d.ts +5 -2
  99. package/lib/typescript/commonjs/dialog/index.web.d.ts.map +1 -1
  100. package/lib/typescript/commonjs/dialog/types.d.ts +70 -15
  101. package/lib/typescript/commonjs/dialog/types.d.ts.map +1 -1
  102. package/lib/typescript/commonjs/index.d.ts +3 -3
  103. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  104. package/lib/typescript/commonjs/index.web.d.ts +3 -3
  105. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/menu/index.d.ts +4 -4
  107. package/lib/typescript/commonjs/menu/index.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/select/index.d.ts.map +1 -1
  109. package/lib/typescript/commonjs/toast/index.d.ts +32 -3
  110. package/lib/typescript/commonjs/toast/index.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/toast/index.web.d.ts +14 -7
  112. package/lib/typescript/commonjs/toast/index.web.d.ts.map +1 -1
  113. package/lib/typescript/module/__tests__/Dialog.test.d.ts +2 -0
  114. package/lib/typescript/module/__tests__/Dialog.test.d.ts.map +1 -0
  115. package/lib/typescript/module/bottom-sheet/index.d.ts +47 -1
  116. package/lib/typescript/module/bottom-sheet/index.d.ts.map +1 -1
  117. package/lib/typescript/module/context-menu/index.d.ts +4 -3
  118. package/lib/typescript/module/context-menu/index.d.ts.map +1 -1
  119. package/lib/typescript/module/dialog/BloomDialogProvider.d.ts +27 -0
  120. package/lib/typescript/module/dialog/BloomDialogProvider.d.ts.map +1 -0
  121. package/lib/typescript/module/dialog/BloomDialogProvider.web.d.ts +15 -0
  122. package/lib/typescript/module/dialog/BloomDialogProvider.web.d.ts.map +1 -0
  123. package/lib/typescript/module/dialog/Dialog.d.ts +37 -10
  124. package/lib/typescript/module/dialog/Dialog.d.ts.map +1 -1
  125. package/lib/typescript/module/dialog/Dialog.web.d.ts +26 -10
  126. package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
  127. package/lib/typescript/module/dialog/SheetShell.d.ts +31 -0
  128. package/lib/typescript/module/dialog/SheetShell.d.ts.map +1 -0
  129. package/lib/typescript/module/dialog/alert-store.d.ts +70 -0
  130. package/lib/typescript/module/dialog/alert-store.d.ts.map +1 -0
  131. package/lib/typescript/module/dialog/alert.d.ts +27 -0
  132. package/lib/typescript/module/dialog/alert.d.ts.map +1 -0
  133. package/lib/typescript/module/dialog/context.d.ts +7 -0
  134. package/lib/typescript/module/dialog/context.d.ts.map +1 -1
  135. package/lib/typescript/module/dialog/index.d.ts +5 -2
  136. package/lib/typescript/module/dialog/index.d.ts.map +1 -1
  137. package/lib/typescript/module/dialog/index.web.d.ts +5 -2
  138. package/lib/typescript/module/dialog/index.web.d.ts.map +1 -1
  139. package/lib/typescript/module/dialog/types.d.ts +70 -15
  140. package/lib/typescript/module/dialog/types.d.ts.map +1 -1
  141. package/lib/typescript/module/index.d.ts +3 -3
  142. package/lib/typescript/module/index.d.ts.map +1 -1
  143. package/lib/typescript/module/index.web.d.ts +3 -3
  144. package/lib/typescript/module/index.web.d.ts.map +1 -1
  145. package/lib/typescript/module/menu/index.d.ts +4 -4
  146. package/lib/typescript/module/menu/index.d.ts.map +1 -1
  147. package/lib/typescript/module/select/index.d.ts.map +1 -1
  148. package/lib/typescript/module/toast/index.d.ts +32 -3
  149. package/lib/typescript/module/toast/index.d.ts.map +1 -1
  150. package/lib/typescript/module/toast/index.web.d.ts +14 -7
  151. package/lib/typescript/module/toast/index.web.d.ts.map +1 -1
  152. package/package.json +3 -14
  153. package/src/__tests__/BottomSheet.test.tsx +149 -2
  154. package/src/__tests__/Dialog.test.tsx +177 -0
  155. package/src/bottom-sheet/index.tsx +367 -83
  156. package/src/context-menu/index.tsx +12 -12
  157. package/src/dialog/BloomDialogProvider.tsx +61 -0
  158. package/src/dialog/BloomDialogProvider.web.tsx +46 -0
  159. package/src/dialog/Dialog.tsx +217 -64
  160. package/src/dialog/Dialog.web.tsx +240 -75
  161. package/src/dialog/SheetShell.tsx +154 -0
  162. package/src/dialog/alert-store.ts +126 -0
  163. package/src/dialog/alert.ts +42 -0
  164. package/src/dialog/context.ts +14 -3
  165. package/src/dialog/index.ts +14 -2
  166. package/src/dialog/index.web.ts +20 -8
  167. package/src/dialog/types.ts +73 -16
  168. package/src/index.ts +17 -3
  169. package/src/index.web.ts +17 -3
  170. package/src/menu/index.tsx +13 -17
  171. package/src/select/index.tsx +30 -30
  172. package/src/toast/index.tsx +55 -11
  173. package/src/toast/index.web.tsx +33 -13
  174. package/lib/commonjs/prompt/Prompt.js +0 -267
  175. package/lib/commonjs/prompt/Prompt.js.map +0 -1
  176. package/lib/commonjs/prompt/index.js +0 -61
  177. package/lib/commonjs/prompt/index.js.map +0 -1
  178. package/lib/module/prompt/Prompt.js +0 -250
  179. package/lib/module/prompt/Prompt.js.map +0 -1
  180. package/lib/module/prompt/index.js +0 -4
  181. package/lib/module/prompt/index.js.map +0 -1
  182. package/lib/typescript/commonjs/prompt/Prompt.d.ts +0 -42
  183. package/lib/typescript/commonjs/prompt/Prompt.d.ts.map +0 -1
  184. package/lib/typescript/commonjs/prompt/index.d.ts +0 -3
  185. package/lib/typescript/commonjs/prompt/index.d.ts.map +0 -1
  186. package/lib/typescript/module/prompt/Prompt.d.ts +0 -42
  187. package/lib/typescript/module/prompt/Prompt.d.ts.map +0 -1
  188. package/lib/typescript/module/prompt/index.d.ts +0 -3
  189. package/lib/typescript/module/prompt/index.d.ts.map +0 -1
  190. package/src/prompt/Prompt.tsx +0 -247
  191. package/src/prompt/index.ts +0 -13
package/README.md CHANGED
@@ -5,7 +5,7 @@ Shared UI component library for the Oxy ecosystem. Built for React Native + Expo
5
5
  ## Install
6
6
 
7
7
  ```sh
8
- npm install @oxyhq/bloom
8
+ bun add @oxyhq/bloom
9
9
  ```
10
10
 
11
11
  ### Peer dependencies
@@ -18,10 +18,10 @@ Required:
18
18
 
19
19
  Optional:
20
20
 
21
- - `@gorhom/bottom-sheet >= 5` (native `Dialog` and `Prompt`) — also requires wrapping the app root with `BottomSheetModalProvider`, see [Dialog](#dialog).
22
21
  - `react-native-reanimated >= 3` (native `Dialog`, `BottomSheet`, Loading `top` variant)
23
22
  - `react-native-gesture-handler >= 2` (native `Dialog`, `BottomSheet`) — also requires wrapping the app root with `GestureHandlerRootView`, see [Dialog](#dialog).
24
23
  - `react-native-svg >= 13` (Avatar `squircle` shape)
24
+ - `sonner >= 2` / `sonner-native >= 0.17` (`toast`)
25
25
 
26
26
  ## Usage
27
27
 
@@ -50,76 +50,107 @@ const theme = useTheme();
50
50
 
51
51
  4 modes: `light`, `dark`, `system`, `adaptive` (uses iOS/Android native dynamic colors when available).
52
52
 
53
- ### Modal components: Dialog, Prompt, BottomSheet
53
+ ### Modal & feedback components: Dialog, BottomSheet, toast / alert
54
54
 
55
- Bloom ships three components for modal/sheet presentation. Pick the one that matches your use case:
55
+ Bloom ships three primitives for modal, sheet, and feedback presentation. Pick the one that matches your use case:
56
56
 
57
57
  | Component | Native | Web | Use when |
58
58
  |-----------|--------|-----|----------|
59
- | `Dialog` | Bottom sheet (Gorhom), dynamic height | Centered modal | You need a modal container with arbitrary content — forms, pickers, custom layouts |
60
- | `Prompt` | 40%-height bottom sheet (Gorhom) | Centered 320px modal | You need a confirmation dialog with title, description, and action buttons |
61
- | `BottomSheet` | Draggable sheet (Bloom's own, no Gorhom) | Same pattern via RN `Modal` | You need a bottom sheet without the Gorhom dependency, or with custom snap/scroll/keyboard control |
62
-
63
- `Prompt` is built on top of `Dialog` (so the provider requirements are the same). `BottomSheet` is a separate, standalone implementation.
59
+ | `Dialog` | Bottom-sheet card (Bloom's own) | Centered modal | Confirmation flows AND custom modal content — same component handles both |
60
+ | `BottomSheet` | Draggable sheet | RN `Modal` polyfill | You need direct control over snap points, scroll handoff, keyboard, detached/flush layout |
61
+ | `toast` / `alert` | Sonner-native + Bloom theme | Sonner + Bloom theme | Passive feedback (`toast`) or one-shot confirmations (`alert`) called imperatively from anywhere |
64
62
 
65
63
  ### Dialog
66
64
 
67
- Platform-adaptive dialogs bottom sheet on native, centered modal overlay on web.
65
+ A single `<Dialog>` component for both confirmation flows AND custom modal content. Bottom-sheet card on native, centered modal on web.
68
66
 
69
- > **Required providers (native).** `Dialog` (and therefore `Prompt`) uses `@gorhom/bottom-sheet` on Android/iOS. Your app root **must** be wrapped with `GestureHandlerRootView` from `react-native-gesture-handler` and `BottomSheetModalProvider` from `@gorhom/bottom-sheet`. Without these, the dialog will silently fail to render.
67
+ > **Required providers (native).** Your app root **must** be wrapped with `GestureHandlerRootView` from `react-native-gesture-handler` for the bottom-sheet pan gestures to work.
70
68
 
71
69
  ```tsx
72
70
  import { GestureHandlerRootView } from 'react-native-gesture-handler';
73
- import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
74
- import { BloomThemeProvider } from '@oxyhq/bloom/theme';
71
+ import { BloomThemeProvider, BloomDialogProvider } from '@oxyhq/bloom';
75
72
 
76
73
  export default function Root() {
77
74
  return (
78
75
  <GestureHandlerRootView style={{ flex: 1 }}>
79
76
  <BloomThemeProvider mode="system" colorPreset="oxy">
80
- <BottomSheetModalProvider>
77
+ <BloomDialogProvider>
81
78
  <App />
82
- </BottomSheetModalProvider>
79
+ </BloomDialogProvider>
83
80
  </BloomThemeProvider>
84
81
  </GestureHandlerRootView>
85
82
  );
86
83
  }
87
84
  ```
88
85
 
89
- Basic usage:
86
+ `BloomDialogProvider` powers the imperative `alert()` helper (see below) — mount it once near the app root.
90
87
 
91
- ```tsx
92
- import * as Dialog from '@oxyhq/bloom/dialog';
88
+ #### Declarative (the 90% case)
93
89
 
94
- function MyComponent() {
95
- const control = Dialog.useDialogControl();
90
+ ```tsx
91
+ import { Dialog, useDialogControl } from '@oxyhq/bloom';
96
92
 
93
+ function SignOutButton() {
94
+ const control = useDialogControl();
97
95
  return (
98
96
  <>
99
- <Button onPress={() => control.open()}>Open</Button>
100
-
101
- <Dialog.Outer control={control} onClose={() => console.log('closed')}>
102
- <Dialog.Handle />
103
- <Dialog.Inner label="My Dialog">
104
- <Text>Dialog content</Text>
105
- </Dialog.Inner>
106
- </Dialog.Outer>
97
+ <Button onPress={() => control.open()}>Sign out</Button>
98
+
99
+ <Dialog
100
+ control={control}
101
+ title="Sign out?"
102
+ description="You'll need to enter your password to sign in again."
103
+ actions={[
104
+ { label: 'Sign out', color: 'destructive', onPress: doSignOut },
105
+ { label: 'Cancel', color: 'cancel' },
106
+ ]}
107
+ />
107
108
  </>
108
109
  );
109
110
  }
110
111
  ```
111
112
 
112
- `Dialog.Outer` props:
113
+ #### Custom content
113
114
 
114
- - `control` from `useDialogControl()`.
115
- - `onClose?` — fires after the dialog has finished closing.
116
- - `testID?`
117
- - `webOptions?: { alignCenter?: boolean }` center the dialog vertically on web instead of anchoring near the top.
118
- - `preventExpansion?: boolean` — on native, snaps the bottom sheet to a fixed `'40%'` height instead of dynamic sizing.
115
+ Provide any JSX as `children`. Combine with `title` to keep a consistent header.
116
+
117
+ ```tsx
118
+ <Dialog control={control} title="Pick a tag">
119
+ <YourCustomBody />
120
+ </Dialog>
121
+ ```
122
+
123
+ #### Pure custom
124
+
125
+ Drop the declarative props entirely — `children` owns every pixel.
126
+
127
+ ```tsx
128
+ <Dialog control={control}>
129
+ <YourEntirelyCustomLayout />
130
+ </Dialog>
131
+ ```
119
132
 
120
- On native, the sheet uses `enablePanDownToClose`, `enableDismissOnClose`, dynamic sizing, and is constrained to a max width of 500px on tablets.
133
+ #### Props
121
134
 
122
- On web, inject the CSS animations into your global styles once:
135
+ - `control` from `useDialogControl()`.
136
+ - `title?: string` — header text.
137
+ - `description?: string` — supporting copy rendered below the title.
138
+ - `actions?: DialogAction[]` — confirmation buttons. Each action accepts:
139
+ - `label: string` — button text.
140
+ - `color?: 'default' | 'cancel' | 'destructive'` — defaults to `'default'`.
141
+ - `onPress?: (e) => void` — invoked after the dialog finishes closing.
142
+ - `disabled?: boolean`
143
+ - `shouldCloseOnPress?: boolean` — defaults to `true`. Set `false` while an async action is in flight.
144
+ - `testID?: string`
145
+ - `children?: React.ReactNode` — custom content rendered after the description.
146
+ - `onClose?: () => void` — fires after the dialog has finished closing.
147
+ - `style?` — applied to the dialog content container.
148
+ - `label?: string` — accessibility label.
149
+ - `testID?: string`
150
+
151
+ #### Web setup
152
+
153
+ Inject the CSS animations into your global styles once:
123
154
 
124
155
  ```tsx
125
156
  import { BLOOM_DIALOG_CSS } from '@oxyhq/bloom/dialog';
@@ -128,66 +159,49 @@ import { BLOOM_DIALOG_CSS } from '@oxyhq/bloom/dialog';
128
159
  <style>{BLOOM_DIALOG_CSS}</style>
129
160
  ```
130
161
 
131
- ### Prompt
132
-
133
- Confirmation dialogs built on top of `Dialog`. On native, constrained to a 40% bottom sheet (Gorhom); on web, a centered 320px modal. Same provider requirements as [Dialog](#dialog).
162
+ ### toast
134
163
 
135
- `Prompt.Action` auto-closes the dialog after `onPress` by default. Pass `shouldCloseOnPress={false}` to keep it open (e.g. while an async operation is in flight).
136
-
137
- `Prompt.Basic` — one-shot confirm dialog:
164
+ Passive notifications, sonner under the hood, themed by bloom.
138
165
 
139
166
  ```tsx
140
- import * as Prompt from '@oxyhq/bloom/prompt';
141
-
142
- function DeleteButton() {
143
- const control = Prompt.usePromptControl();
144
-
145
- return (
146
- <>
147
- <Button onPress={() => control.open()}>Delete</Button>
148
-
149
- <Prompt.Basic
150
- control={control}
151
- title="Delete item?"
152
- description="This action cannot be undone."
153
- confirmButtonCta="Delete"
154
- confirmButtonColor="negative"
155
- onConfirm={() => handleDelete()}
156
- />
157
- </>
158
- );
159
- }
167
+ import { toast } from '@oxyhq/bloom';
168
+ import { ToastOutlet } from '@oxyhq/bloom/toast';
169
+
170
+ // Mount once near the app root, inside BloomThemeProvider:
171
+ <ToastOutlet />
172
+
173
+ // Anywhere in your app:
174
+ toast('Saved');
175
+ toast.success('Profile updated');
176
+ toast.error('Network error', { duration: 5000 });
177
+ toast.warning('Please verify your email');
178
+ toast.info('A new version is available');
179
+ toast.dismiss(id);
160
180
  ```
161
181
 
162
- `Prompt.Basic` props:
182
+ `toast(content, options?)` accepts strings or React elements. `options.type` (`'default' | 'success' | 'error' | 'warning' | 'info'`) is overridden by the typed helpers above.
163
183
 
164
- - `control` — from `usePromptControl()`.
165
- - `title: string`
166
- - `description?: string`
167
- - `confirmButtonCta?: string` — defaults to `'Confirm'`.
168
- - `cancelButtonCta?: string` — defaults to `'Cancel'`.
169
- - `confirmButtonColor?: ActionColor` — defaults to `'primary'`.
170
- - `onConfirm: (e) => void`
171
- - `showCancel?: boolean` — defaults to `true`.
184
+ ### alert()
172
185
 
173
- Or compose with the compound components:
186
+ Imperative one-shot confirmation dialogs, matching React Native's `Alert.alert(title, message?, buttons?)` signature. Calls are queued and rendered through `BloomDialogProvider` — you can call `alert()` from anywhere, including before the provider mounts (alerts queue and drain on subscribe).
174
187
 
175
188
  ```tsx
176
- <Prompt.Outer control={control}>
177
- <Prompt.Content>
178
- <Prompt.TitleText>Are you sure?</Prompt.TitleText>
179
- <Prompt.DescriptionText>This is permanent.</Prompt.DescriptionText>
180
- </Prompt.Content>
181
- <Prompt.Actions>
182
- <Prompt.Action cta="Confirm" color="negative" onPress={handleConfirm} />
183
- <Prompt.Cancel />
184
- </Prompt.Actions>
185
- </Prompt.Outer>
189
+ import { alert } from '@oxyhq/bloom';
190
+
191
+ alert('Sign out?', 'Are you sure you want to sign out of this device?', [
192
+ { text: 'Cancel', style: 'cancel' },
193
+ { text: 'Sign out', style: 'destructive', onPress: doSignOut },
194
+ ]);
195
+
196
+ // Single OK button (default when no buttons passed):
197
+ alert('Saved');
186
198
  ```
187
199
 
188
- Exports: `usePromptControl`, `Outer`, `Content`, `TitleText`, `DescriptionText`, `Actions`, `Action`, `Cancel`, `Basic`.
200
+ Each button:
189
201
 
190
- `ActionColor`: `'primary' | 'primary_subtle' | 'secondary' | 'negative' | 'negative_subtle'`.
202
+ - `text: string` required label.
203
+ - `style?: 'default' | 'cancel' | 'destructive'` — defaults to `'default'`.
204
+ - `onPress?: () => void` — fires after the dialog finishes closing.
191
205
 
192
206
  ### BottomSheet
193
207
 
@@ -222,9 +236,37 @@ function Example() {
222
236
  - `enableHandlePanningGesture?: boolean` — defaults to `true`.
223
237
  - `onDismissAttempt?: () => boolean` — return `false` to veto a dismiss attempt.
224
238
  - `detached?: boolean` — when `true`, the sheet floats with horizontal margins and rounded corners on all sides; when `false`, it's flush to the bottom edges with rounded top corners only.
239
+ - `showHandle?: boolean` — defaults to `true`. Toggles the drag handle pill at the top of the sheet.
240
+ - `backdropOpacity?: number` — opacity (0–1) of the dimming backdrop once fully visible. Defaults to `0.5`. Use a higher value (e.g. `0.7`) when stacking a sheet over another sheet.
225
241
  - `backgroundComponent?` — custom background renderer.
226
242
  - `backdropComponent?` — custom backdrop renderer.
227
243
  - `style?`
244
+ - `scrollable?: boolean` — defaults to `true`. When `false`, renders `children` directly without the internal `Animated.ScrollView` wrapper. **Required when the sheet's content owns its own scrolling primitive** (e.g. `FlatList`, `SectionList`, or any `VirtualizedList`) — nesting a virtualized list inside the internal ScrollView breaks windowing and triggers a React Native warning. Combine with `manualActivation` so the handle stays draggable while the inner list owns the scroll.
245
+ - `manualActivation?: boolean` — defaults to `false`. When `true`, the body pan uses RNGH's `manualActivation` and only activates when (a) the inner ScrollView is at the top AND (b) the user has moved their finger downward by > 8dp. This is the `@gorhom/bottom-sheet` coordination model — recommended for sheets that contain a scrolling region on Android (the legacy always-active pan can steal vertical events from the inner scroller). Enabling this also gives the drag handle its own dedicated, unconditionally-active gesture so users can always grab the handle even mid-scroll.
246
+ - `dynamicBackdrop?: boolean` — defaults to `false`. When `true`, the backdrop dims proportionally to drag distance — fades from full `backdropOpacity` (sheet at rest) to 30% as the sheet is pulled down 40% of the screen height. This is the iOS Photos / iMessage drag-to-dismiss look. The base `backdropOpacity` still controls the resting dim.
247
+ - `handleComponent?: () => React.ReactNode` — custom drag-handle renderer. When provided (and `showHandle` is `true`), replaces the default 36×5 pill. In `manualActivation` mode the rendered handle sits inside the dedicated handle hit-area and gesture detector so it remains unconditionally draggable.
248
+
249
+ **Pattern: sheet with a `FlatList` inside.** Use `scrollable={false}` so the BottomSheet doesn't wrap the list in its own ScrollView, plus `manualActivation` so the drag handle remains the dedicated drag-to-dismiss surface while the list owns vertical scroll:
250
+
251
+ ```tsx
252
+ <BottomSheet ref={sheetRef} scrollable={false} manualActivation>
253
+ <FlatList data={items} renderItem={renderItem} />
254
+ </BottomSheet>
255
+ ```
256
+
257
+ **Pattern: iOS Photos-style backdrop dim.** Combine `manualActivation` (so the inner photo grid keeps scroll ownership) with `dynamicBackdrop` (so the overlay fades as the user pulls down):
258
+
259
+ ```tsx
260
+ <BottomSheet
261
+ ref={sheetRef}
262
+ scrollable={false}
263
+ manualActivation
264
+ dynamicBackdrop
265
+ backdropOpacity={0.85}
266
+ >
267
+ <PhotoGrid />
268
+ </BottomSheet>
269
+ ```
228
270
 
229
271
  ### Button
230
272
 
@@ -368,9 +410,9 @@ import {
368
410
 
369
411
  ```ts
370
412
  import { BloomThemeProvider, useTheme } from '@oxyhq/bloom/theme';
371
- import * as Dialog from '@oxyhq/bloom/dialog';
372
- import * as Prompt from '@oxyhq/bloom/prompt';
413
+ import { Dialog, BloomDialogProvider, alert, useDialogControl } from '@oxyhq/bloom/dialog';
373
414
  import { BottomSheet, type BottomSheetRef } from '@oxyhq/bloom/bottom-sheet';
415
+ import { toast, ToastOutlet } from '@oxyhq/bloom/toast';
374
416
  import { Button, IconButton } from '@oxyhq/bloom/button';
375
417
  import { GroupedButtons } from '@oxyhq/bloom/grouped-buttons';
376
418
  import { Divider } from '@oxyhq/bloom/divider';
@@ -385,10 +427,11 @@ import { PromptInput, PromptInputTextarea } from '@oxyhq/bloom/prompt-input';
385
427
  ## Development
386
428
 
387
429
  ```sh
388
- npm install
389
- npm run build # react-native-builder-bob
390
- npm run typescript # type-check
391
- npm run clean # remove lib/
430
+ bun install
431
+ bun run build # react-native-builder-bob
432
+ bun run typescript # type-check
433
+ bun run test # jest
434
+ bun run clean # remove lib/
392
435
  ```
393
436
 
394
437
  ## License