@cdx-ui/components 0.1.6 → 0.1.7

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 (228) hide show
  1. package/coverage.svg +1 -1
  2. package/lib/commonjs/components/Alert/index.js.map +1 -1
  3. package/lib/commonjs/components/Avatar/index.js +6 -4
  4. package/lib/commonjs/components/Avatar/index.js.map +1 -1
  5. package/lib/commonjs/components/Badge/index.js.map +1 -1
  6. package/lib/commonjs/components/Badge/styles.js +2 -2
  7. package/lib/commonjs/components/BottomSheet/index.js +106 -91
  8. package/lib/commonjs/components/BottomSheet/index.js.map +1 -1
  9. package/lib/commonjs/components/BottomSheet/styles.js +1 -12
  10. package/lib/commonjs/components/BottomSheet/styles.js.map +1 -1
  11. package/lib/commonjs/components/Button/index.js.map +1 -1
  12. package/lib/commonjs/components/Card/index.js.map +1 -1
  13. package/lib/commonjs/components/Dialog/index.js.map +1 -1
  14. package/lib/commonjs/components/Divider/index.js.map +1 -1
  15. package/lib/commonjs/components/Form/styles.js +1 -1
  16. package/lib/commonjs/components/Form/styles.js.map +1 -1
  17. package/lib/commonjs/components/FullScreenDialog/animation.js +46 -0
  18. package/lib/commonjs/components/FullScreenDialog/animation.js.map +1 -0
  19. package/lib/commonjs/components/FullScreenDialog/index.js +343 -0
  20. package/lib/commonjs/components/FullScreenDialog/index.js.map +1 -0
  21. package/lib/commonjs/components/FullScreenDialog/styles.js +14 -0
  22. package/lib/commonjs/components/FullScreenDialog/styles.js.map +1 -0
  23. package/lib/commonjs/components/IconButton/styles.js +7 -5
  24. package/lib/commonjs/components/IconButton/styles.js.map +1 -1
  25. package/lib/commonjs/components/InsuranceDisclosure/index.js.map +1 -1
  26. package/lib/commonjs/components/Link/index.js.map +1 -1
  27. package/lib/commonjs/components/ListItem/index.js +2 -0
  28. package/lib/commonjs/components/ListItem/index.js.map +1 -1
  29. package/lib/commonjs/components/ListItem/styles.js +6 -0
  30. package/lib/commonjs/components/ListItem/styles.js.map +1 -1
  31. package/lib/commonjs/components/ProgressSegmented/index.js.map +1 -1
  32. package/lib/commonjs/components/Radio/index.js.map +1 -1
  33. package/lib/commonjs/components/ScopedTheme/index.js +11 -3
  34. package/lib/commonjs/components/ScopedTheme/index.js.map +1 -1
  35. package/lib/commonjs/components/Skeleton/index.js.map +1 -1
  36. package/lib/commonjs/components/Stack/HStack.js.map +1 -1
  37. package/lib/commonjs/components/Stack/VStack.js.map +1 -1
  38. package/lib/commonjs/components/Textarea/index.js.map +1 -1
  39. package/lib/commonjs/components/Tile/index.js.map +1 -1
  40. package/lib/commonjs/components/Toast/index.js +40 -12
  41. package/lib/commonjs/components/Toast/index.js.map +1 -1
  42. package/lib/commonjs/components/Toast/styles.js +15 -4
  43. package/lib/commonjs/components/Toast/styles.js.map +1 -1
  44. package/lib/commonjs/components/index.js +12 -0
  45. package/lib/commonjs/components/index.js.map +1 -1
  46. package/lib/commonjs/components/picker/styles.js +6 -2
  47. package/lib/commonjs/components/picker/styles.js.map +1 -1
  48. package/lib/commonjs/components/sheet-header/index.js +97 -0
  49. package/lib/commonjs/components/sheet-header/index.js.map +1 -0
  50. package/lib/commonjs/components/sheet-header/styles.js +28 -0
  51. package/lib/commonjs/components/sheet-header/styles.js.map +1 -0
  52. package/lib/commonjs/figma/BottomSheet.figma.js +1 -1
  53. package/lib/commonjs/providers/toast/provider.js +44 -11
  54. package/lib/commonjs/providers/toast/provider.js.map +1 -1
  55. package/lib/module/components/Alert/index.js.map +1 -1
  56. package/lib/module/components/Avatar/index.js +6 -4
  57. package/lib/module/components/Avatar/index.js.map +1 -1
  58. package/lib/module/components/Badge/index.js.map +1 -1
  59. package/lib/module/components/Badge/styles.js +2 -2
  60. package/lib/module/components/BottomSheet/index.js +110 -95
  61. package/lib/module/components/BottomSheet/index.js.map +1 -1
  62. package/lib/module/components/BottomSheet/styles.js +0 -11
  63. package/lib/module/components/BottomSheet/styles.js.map +1 -1
  64. package/lib/module/components/Button/index.js.map +1 -1
  65. package/lib/module/components/Card/index.js.map +1 -1
  66. package/lib/module/components/Dialog/index.js.map +1 -1
  67. package/lib/module/components/Divider/index.js.map +1 -1
  68. package/lib/module/components/Form/styles.js +1 -1
  69. package/lib/module/components/Form/styles.js.map +1 -1
  70. package/lib/module/components/FullScreenDialog/animation.js +41 -0
  71. package/lib/module/components/FullScreenDialog/animation.js.map +1 -0
  72. package/lib/module/components/FullScreenDialog/index.js +338 -0
  73. package/lib/module/components/FullScreenDialog/index.js.map +1 -0
  74. package/lib/module/components/FullScreenDialog/styles.js +10 -0
  75. package/lib/module/components/FullScreenDialog/styles.js.map +1 -0
  76. package/lib/module/components/IconButton/styles.js +7 -4
  77. package/lib/module/components/IconButton/styles.js.map +1 -1
  78. package/lib/module/components/InsuranceDisclosure/index.js.map +1 -1
  79. package/lib/module/components/Link/index.js.map +1 -1
  80. package/lib/module/components/ListItem/index.js +2 -0
  81. package/lib/module/components/ListItem/index.js.map +1 -1
  82. package/lib/module/components/ListItem/styles.js +6 -0
  83. package/lib/module/components/ListItem/styles.js.map +1 -1
  84. package/lib/module/components/ProgressSegmented/index.js.map +1 -1
  85. package/lib/module/components/Radio/index.js.map +1 -1
  86. package/lib/module/components/ScopedTheme/index.js +11 -3
  87. package/lib/module/components/ScopedTheme/index.js.map +1 -1
  88. package/lib/module/components/Skeleton/index.js.map +1 -1
  89. package/lib/module/components/Stack/HStack.js.map +1 -1
  90. package/lib/module/components/Stack/VStack.js.map +1 -1
  91. package/lib/module/components/Textarea/index.js.map +1 -1
  92. package/lib/module/components/Tile/index.js.map +1 -1
  93. package/lib/module/components/Toast/index.js +43 -15
  94. package/lib/module/components/Toast/index.js.map +1 -1
  95. package/lib/module/components/Toast/styles.js +14 -3
  96. package/lib/module/components/Toast/styles.js.map +1 -1
  97. package/lib/module/components/index.js +1 -0
  98. package/lib/module/components/index.js.map +1 -1
  99. package/lib/module/components/picker/styles.js +6 -2
  100. package/lib/module/components/picker/styles.js.map +1 -1
  101. package/lib/module/components/sheet-header/index.js +94 -0
  102. package/lib/module/components/sheet-header/index.js.map +1 -0
  103. package/lib/module/components/sheet-header/styles.js +25 -0
  104. package/lib/module/components/sheet-header/styles.js.map +1 -0
  105. package/lib/module/figma/BottomSheet.figma.js +1 -1
  106. package/lib/module/providers/toast/provider.js +44 -11
  107. package/lib/module/providers/toast/provider.js.map +1 -1
  108. package/lib/typescript/components/Alert/index.d.ts +4 -1
  109. package/lib/typescript/components/Alert/index.d.ts.map +1 -1
  110. package/lib/typescript/components/Avatar/index.d.ts +10 -4
  111. package/lib/typescript/components/Avatar/index.d.ts.map +1 -1
  112. package/lib/typescript/components/Badge/index.d.ts +34 -0
  113. package/lib/typescript/components/Badge/index.d.ts.map +1 -1
  114. package/lib/typescript/components/BottomSheet/index.d.ts +12 -1
  115. package/lib/typescript/components/BottomSheet/index.d.ts.map +1 -1
  116. package/lib/typescript/components/BottomSheet/styles.d.ts +0 -5
  117. package/lib/typescript/components/BottomSheet/styles.d.ts.map +1 -1
  118. package/lib/typescript/components/Button/index.d.ts +9 -1
  119. package/lib/typescript/components/Button/index.d.ts.map +1 -1
  120. package/lib/typescript/components/Card/index.d.ts +14 -0
  121. package/lib/typescript/components/Card/index.d.ts.map +1 -1
  122. package/lib/typescript/components/Chip/index.d.ts +1 -1
  123. package/lib/typescript/components/CurrencyInput/index.d.ts +2 -2
  124. package/lib/typescript/components/CurrencyInput/index.d.ts.map +1 -1
  125. package/lib/typescript/components/Dialog/index.d.ts +10 -0
  126. package/lib/typescript/components/Dialog/index.d.ts.map +1 -1
  127. package/lib/typescript/components/Dialog/styles.d.ts +1 -0
  128. package/lib/typescript/components/Dialog/styles.d.ts.map +1 -1
  129. package/lib/typescript/components/Divider/index.d.ts +5 -0
  130. package/lib/typescript/components/Divider/index.d.ts.map +1 -1
  131. package/lib/typescript/components/Drawer/styles.d.ts +1 -1
  132. package/lib/typescript/components/Form/styles.d.ts.map +1 -1
  133. package/lib/typescript/components/FullScreenDialog/animation.d.ts +9 -0
  134. package/lib/typescript/components/FullScreenDialog/animation.d.ts.map +1 -0
  135. package/lib/typescript/components/FullScreenDialog/index.d.ts +85 -0
  136. package/lib/typescript/components/FullScreenDialog/index.d.ts.map +1 -0
  137. package/lib/typescript/components/FullScreenDialog/styles.d.ts +6 -0
  138. package/lib/typescript/components/FullScreenDialog/styles.d.ts.map +1 -0
  139. package/lib/typescript/components/IconButton/styles.d.ts +5 -0
  140. package/lib/typescript/components/IconButton/styles.d.ts.map +1 -1
  141. package/lib/typescript/components/Input/index.d.ts +1 -1
  142. package/lib/typescript/components/InsuranceDisclosure/index.d.ts +1 -0
  143. package/lib/typescript/components/InsuranceDisclosure/index.d.ts.map +1 -1
  144. package/lib/typescript/components/Link/index.d.ts +2 -0
  145. package/lib/typescript/components/Link/index.d.ts.map +1 -1
  146. package/lib/typescript/components/ListItem/index.d.ts +43 -4
  147. package/lib/typescript/components/ListItem/index.d.ts.map +1 -1
  148. package/lib/typescript/components/ListItem/styles.d.ts +2 -0
  149. package/lib/typescript/components/ListItem/styles.d.ts.map +1 -1
  150. package/lib/typescript/components/ProgressSegmented/index.d.ts +10 -3
  151. package/lib/typescript/components/ProgressSegmented/index.d.ts.map +1 -1
  152. package/lib/typescript/components/Radio/index.d.ts +1 -0
  153. package/lib/typescript/components/Radio/index.d.ts.map +1 -1
  154. package/lib/typescript/components/ScopedTheme/index.d.ts +11 -3
  155. package/lib/typescript/components/ScopedTheme/index.d.ts.map +1 -1
  156. package/lib/typescript/components/Skeleton/index.d.ts +5 -1
  157. package/lib/typescript/components/Skeleton/index.d.ts.map +1 -1
  158. package/lib/typescript/components/Stack/HStack.d.ts +6 -0
  159. package/lib/typescript/components/Stack/HStack.d.ts.map +1 -1
  160. package/lib/typescript/components/Stack/VStack.d.ts +6 -0
  161. package/lib/typescript/components/Stack/VStack.d.ts.map +1 -1
  162. package/lib/typescript/components/Switch/index.d.ts +1 -1
  163. package/lib/typescript/components/Tabs/index.d.ts +2 -2
  164. package/lib/typescript/components/Textarea/index.d.ts +1 -0
  165. package/lib/typescript/components/Textarea/index.d.ts.map +1 -1
  166. package/lib/typescript/components/Tile/index.d.ts +49 -0
  167. package/lib/typescript/components/Tile/index.d.ts.map +1 -1
  168. package/lib/typescript/components/Toast/index.d.ts +15 -0
  169. package/lib/typescript/components/Toast/index.d.ts.map +1 -1
  170. package/lib/typescript/components/Toast/styles.d.ts +10 -0
  171. package/lib/typescript/components/Toast/styles.d.ts.map +1 -1
  172. package/lib/typescript/components/Toast/types.d.ts +9 -0
  173. package/lib/typescript/components/Toast/types.d.ts.map +1 -1
  174. package/lib/typescript/components/index.d.ts +1 -0
  175. package/lib/typescript/components/index.d.ts.map +1 -1
  176. package/lib/typescript/components/picker/styles.d.ts +5 -2
  177. package/lib/typescript/components/picker/styles.d.ts.map +1 -1
  178. package/lib/typescript/components/sheet-header/index.d.ts +24 -0
  179. package/lib/typescript/components/sheet-header/index.d.ts.map +1 -0
  180. package/lib/typescript/components/sheet-header/styles.d.ts +12 -0
  181. package/lib/typescript/components/sheet-header/styles.d.ts.map +1 -0
  182. package/lib/typescript/providers/toast/provider.d.ts +1 -1
  183. package/lib/typescript/providers/toast/provider.d.ts.map +1 -1
  184. package/lib/typescript/providers/toast/types.d.ts +16 -4
  185. package/lib/typescript/providers/toast/types.d.ts.map +1 -1
  186. package/package.json +5 -5
  187. package/src/components/Alert/index.tsx +4 -1
  188. package/src/components/Avatar/index.tsx +10 -4
  189. package/src/components/Badge/index.tsx +34 -0
  190. package/src/components/Badge/styles.ts +2 -2
  191. package/src/components/BottomSheet/index.tsx +156 -125
  192. package/src/components/BottomSheet/styles.ts +1 -22
  193. package/src/components/Button/index.tsx +9 -1
  194. package/src/components/Card/index.tsx +14 -0
  195. package/src/components/Chip/index.tsx +1 -1
  196. package/src/components/CurrencyInput/index.tsx +2 -2
  197. package/src/components/Dialog/index.tsx +10 -0
  198. package/src/components/Dialog/styles.ts +1 -0
  199. package/src/components/Divider/index.tsx +5 -0
  200. package/src/components/Form/styles.ts +1 -1
  201. package/src/components/FullScreenDialog/animation.ts +22 -0
  202. package/src/components/FullScreenDialog/index.tsx +484 -0
  203. package/src/components/FullScreenDialog/styles.ts +23 -0
  204. package/src/components/IconButton/styles.ts +7 -4
  205. package/src/components/Input/index.tsx +1 -1
  206. package/src/components/InsuranceDisclosure/index.tsx +1 -0
  207. package/src/components/Link/index.tsx +2 -0
  208. package/src/components/ListItem/index.tsx +48 -4
  209. package/src/components/ListItem/styles.ts +7 -0
  210. package/src/components/ProgressSegmented/index.tsx +10 -3
  211. package/src/components/Radio/index.tsx +1 -0
  212. package/src/components/ScopedTheme/index.tsx +11 -3
  213. package/src/components/Skeleton/index.tsx +5 -1
  214. package/src/components/Stack/HStack.tsx +6 -0
  215. package/src/components/Stack/VStack.tsx +6 -0
  216. package/src/components/Switch/index.tsx +1 -1
  217. package/src/components/Tabs/index.tsx +2 -2
  218. package/src/components/Textarea/index.tsx +1 -0
  219. package/src/components/Tile/index.tsx +49 -0
  220. package/src/components/Toast/index.tsx +55 -9
  221. package/src/components/Toast/styles.ts +14 -3
  222. package/src/components/Toast/types.ts +9 -0
  223. package/src/components/index.ts +1 -0
  224. package/src/components/picker/styles.ts +6 -2
  225. package/src/components/sheet-header/index.tsx +148 -0
  226. package/src/components/sheet-header/styles.ts +28 -0
  227. package/src/providers/toast/provider.tsx +47 -13
  228. package/src/providers/toast/types.ts +16 -4
@@ -1,5 +1,15 @@
1
- import React, { forwardRef, useCallback, useContext, useRef, type ReactNode } from 'react';
2
- import { Dimensions, View, ViewStyle } from 'react-native';
1
+ import React, {
2
+ forwardRef,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useId,
7
+ useImperativeHandle,
8
+ useRef,
9
+ useState,
10
+ type ReactNode,
11
+ } from 'react';
12
+ import { BackHandler, Dimensions, Platform, View, ViewStyle } from 'react-native';
3
13
  import { SafeAreaInsetsContext, type EdgeInsets } from 'react-native-safe-area-context';
4
14
  import { useResolveClassNames } from 'uniwind';
5
15
  import {
@@ -14,16 +24,13 @@ import {
14
24
  type BottomSheetFooterProps as GorhomBottomSheetFooterProps,
15
25
  type BottomSheetModalProps as GorhomBottomSheetModalProps,
16
26
  } from '@gorhom/bottom-sheet';
27
+ import { useEscapeKey, useOverlayStackRegistration } from '@cdx-ui/primitives';
17
28
  import { cn } from '@cdx-ui/utils';
18
- import { ArrowBack, Close } from '@cdx-ui/icons';
19
- import { Heading } from '../Heading';
20
- import { IconButton } from '../IconButton';
21
- import { Text } from '../Text';
29
+ import { SheetHeader } from '../sheet-header';
22
30
  import {
23
31
  bottomSheetBackgroundClassName,
24
32
  bottomSheetContentVariants,
25
33
  bottomSheetFooterVariants,
26
- bottomSheetHeaderVariants,
27
34
  bottomSheetModalContainerClassName,
28
35
  } from './styles';
29
36
 
@@ -37,131 +44,52 @@ function useSafeInsets(): EdgeInsets {
37
44
  export type BottomSheetModalRef = React.ComponentRef<typeof BottomSheetModal>;
38
45
 
39
46
  export interface BottomSheetHeaderProps {
47
+ /** Title text. Ignored when `children` is set. */
40
48
  title?: string;
49
+ /** Secondary line under the title. Has no effect without `title`, or when `children` is set. */
41
50
  subtitle?: string;
51
+ /** Custom header content. Replaces `title` and `subtitle`. */
42
52
  children?: ReactNode;
43
53
  className?: string;
54
+ /** Shows the close button when set. */
44
55
  onClose?: () => void;
56
+ /** Accessible name for the close button. @default Close */
45
57
  closeAccessibilityLabel?: string;
58
+ /** Shows the back button when set. */
46
59
  onBack?: () => void;
60
+ /** Accessible name for the back button. @default Go back */
47
61
  backAccessibilityLabel?: string;
48
62
  }
49
63
 
50
- function BottomSheetHeaderComponent(
51
- {
52
- title,
53
- subtitle,
54
- children,
55
- className,
56
- onClose,
57
- closeAccessibilityLabel,
58
- onBack,
59
- backAccessibilityLabel = 'Go back',
60
- }: BottomSheetHeaderProps,
61
- ref: React.Ref<View>,
62
- ) {
63
- const hasTitle = title !== undefined && title !== '';
64
- const hasContent = hasTitle || !!children || !!onBack;
65
- const computedClassName = cn(bottomSheetHeaderVariants({ hasTitle: hasContent }), className);
66
- return (
67
- <View ref={ref} className={computedClassName}>
68
- <BottomSheetBackButton
69
- className={cn(!onBack && 'opacity-0')}
70
- onPress={onBack}
71
- {...(backAccessibilityLabel !== undefined && {
72
- accessibilityLabel: backAccessibilityLabel,
73
- })}
74
- />
75
- <View className="flex-1">
76
- {children ??
77
- (hasTitle ? (
78
- <View>
79
- <Heading size="sm" className="text-center">
80
- {title}
81
- </Heading>
82
- {subtitle ? (
83
- <Text size="sm" className="text-center text-content-secondary">
84
- {subtitle}
85
- </Text>
86
- ) : null}
87
- </View>
88
- ) : null)}
89
- </View>
90
- <BottomSheetCloseButton
91
- className={cn(!onClose && 'opacity-0')}
92
- onPress={onClose}
93
- {...(closeAccessibilityLabel !== undefined && {
94
- accessibilityLabel: closeAccessibilityLabel,
95
- })}
96
- />
97
- </View>
98
- );
99
- }
100
-
101
- BottomSheetHeaderComponent.displayName = 'BottomSheet.Header';
102
-
103
- const BottomSheetHeader = forwardRef<View, BottomSheetHeaderProps>(BottomSheetHeaderComponent);
104
- BottomSheetHeader.displayName = 'BottomSheet.Header';
105
-
106
- // =============================================================================
107
- // BACKBUTTON
108
- // =============================================================================
109
- interface BottomSheetBackButtonProps {
110
- onPress?: () => void;
111
- className?: string;
112
- accessibilityLabel?: string;
113
- }
114
-
115
- function BottomSheetBackButton({
116
- onPress,
117
- className,
118
- accessibilityLabel = 'Go back',
119
- }: Readonly<BottomSheetBackButtonProps>) {
120
- return (
121
- <IconButton
122
- as={ArrowBack}
123
- variant="solid"
124
- color="neutral"
125
- size="default"
126
- onPress={onPress}
127
- hitSlop={12}
128
- accessibilityLabel={accessibilityLabel}
129
- className={className}
130
- />
131
- );
132
- }
133
-
134
- BottomSheetBackButton.displayName = 'BottomSheet.BackButton';
135
-
136
- // =============================================================================
137
- // CLOSEBUTTON
138
- // =============================================================================
139
- interface BottomSheetCloseButtonProps {
140
- onPress?: () => void;
141
- className?: string;
142
- accessibilityLabel?: string;
143
- }
144
-
145
- function BottomSheetCloseButton({
146
- onPress,
147
- className,
148
- accessibilityLabel = 'Close',
149
- }: Readonly<BottomSheetCloseButtonProps>) {
150
- return (
151
- <IconButton
152
- as={Close}
153
- variant="solid"
154
- color="neutral"
155
- size="default"
156
- onPress={onPress}
157
- hitSlop={12}
158
- accessibilityLabel={accessibilityLabel}
64
+ const BottomSheetHeader = forwardRef<View, BottomSheetHeaderProps>(
65
+ (
66
+ {
67
+ title,
68
+ subtitle,
69
+ children,
70
+ className,
71
+ onClose,
72
+ closeAccessibilityLabel,
73
+ onBack,
74
+ backAccessibilityLabel,
75
+ },
76
+ ref,
77
+ ) => (
78
+ <SheetHeader
79
+ ref={ref}
80
+ title={title}
81
+ subtitle={subtitle}
159
82
  className={className}
160
- />
161
- );
162
- }
163
-
164
- BottomSheetCloseButton.displayName = 'BottomSheet.CloseButton';
83
+ onClose={onClose}
84
+ closeAccessibilityLabel={closeAccessibilityLabel}
85
+ onBack={onBack}
86
+ backAccessibilityLabel={backAccessibilityLabel}
87
+ >
88
+ {children}
89
+ </SheetHeader>
90
+ ),
91
+ );
92
+ BottomSheetHeader.displayName = 'BottomSheet.Header';
165
93
 
166
94
  // =============================================================================
167
95
  // CONTENT
@@ -170,6 +98,7 @@ BottomSheetCloseButton.displayName = 'BottomSheet.CloseButton';
170
98
  export interface BottomSheetContentProps {
171
99
  children?: ReactNode;
172
100
  className?: string;
101
+ /** Adds bottom padding equal to the safe-area inset. */
173
102
  hasSafeAreaInset?: boolean;
174
103
  style?: ViewStyle;
175
104
  }
@@ -228,7 +157,10 @@ BottomSheetFooter.displayName = 'BottomSheet.Footer';
228
157
  export interface BottomSheetModalProps extends GorhomBottomSheetModalProps {
229
158
  /** Show the drag handle indicator. When false, `handleComponent` is set to null. @default true */
230
159
  handleVisible?: boolean;
231
- /** Footer content rendered via gorhom's BottomSheetFooter for proper animated positioning and safe-area handling. */
160
+ /**
161
+ * Footer content rendered via gorhom's BottomSheetFooter for animated positioning and safe-area handling.
162
+ * Pass it through this prop; a footer placed as a child does not animate with the sheet.
163
+ */
232
164
  footer?: ReactNode;
233
165
  }
234
166
 
@@ -253,7 +185,7 @@ const CustomBackground = (props: React.ComponentPropsWithoutRef<typeof View>) =>
253
185
  const BottomSheetModalStyled = forwardRef<
254
186
  React.ComponentRef<typeof BottomSheetModal>,
255
187
  BottomSheetModalProps
256
- >(({ handleVisible = true, footer, onDismiss, onChange, ...rest }, ref) => {
188
+ >(({ handleVisible = true, footer, onDismiss, onChange, onAnimate, ...rest }, ref) => {
257
189
  const renderFooter = useCallback(
258
190
  (props: GorhomBottomSheetFooterProps) => (
259
191
  <GorhomBottomSheetFooter {...props}>{footer}</GorhomBottomSheetFooter>
@@ -261,9 +193,67 @@ const BottomSheetModalStyled = forwardRef<
261
193
  [footer],
262
194
  );
263
195
 
196
+ const modalRef = useRef<BottomSheetModalRef | null>(null);
264
197
  const dismissedRef = useRef(false);
198
+ const sheetMountedRef = useRef(false);
199
+ const mountPendingRef = useRef(false);
200
+ const pendingDismissRef = useRef<Parameters<BottomSheetModalRef['dismiss']> | null>(null);
201
+ const [presented, setPresented] = useState(false);
202
+
203
+ // Must stay a callback ref: gorhom hands this ref to its modal provider, which
204
+ // reads `.current` to minimize and restore stacked sheets. A ref object would
205
+ // switch that stacking on.
206
+ const setModalRef = useCallback((instance: BottomSheetModalRef | null) => {
207
+ modalRef.current = instance;
208
+ }, []);
209
+
210
+ // gorhom mounts the sheet a frame after `present()` and silently drops a
211
+ // `dismiss()` that arrives before then, never reporting `onDismiss`. Holding
212
+ // it until the sheet mounts keeps the early Escape/back claim releasable.
213
+ const dismissSheet = useCallback((...args: Parameters<BottomSheetModalRef['dismiss']>) => {
214
+ if (mountPendingRef.current) {
215
+ pendingDismissRef.current = args;
216
+ return;
217
+ }
218
+ modalRef.current?.dismiss(...args);
219
+ }, []);
220
+
221
+ const markSheetMounted = useCallback(() => {
222
+ sheetMountedRef.current = true;
223
+ if (!mountPendingRef.current) return;
224
+ mountPendingRef.current = false;
225
+ const pendingDismiss = pendingDismissRef.current;
226
+ pendingDismissRef.current = null;
227
+ if (pendingDismiss) modalRef.current?.dismiss(...pendingDismiss);
228
+ }, []);
229
+
230
+ // `present()` claims Escape and back immediately; waiting for the first
231
+ // `onChange` would leave the overlay beneath in charge for the whole open animation.
232
+ useImperativeHandle(
233
+ ref,
234
+ (): BottomSheetModalRef => ({
235
+ present: (...args) => {
236
+ dismissedRef.current = false;
237
+ if (!sheetMountedRef.current) mountPendingRef.current = true;
238
+ setPresented(true);
239
+ modalRef.current?.present(...args);
240
+ },
241
+ dismiss: dismissSheet,
242
+ snapToIndex: (...args) => modalRef.current?.snapToIndex(...args),
243
+ snapToPosition: (...args) => modalRef.current?.snapToPosition(...args),
244
+ expand: (...args) => modalRef.current?.expand(...args),
245
+ collapse: (...args) => modalRef.current?.collapse(...args),
246
+ close: (...args) => modalRef.current?.close(...args),
247
+ forceClose: (...args) => modalRef.current?.forceClose(...args),
248
+ }),
249
+ [dismissSheet],
250
+ );
265
251
 
266
252
  const handleDismiss = useCallback(() => {
253
+ sheetMountedRef.current = false;
254
+ mountPendingRef.current = false;
255
+ pendingDismissRef.current = null;
256
+ setPresented(false);
267
257
  if (dismissedRef.current) return;
268
258
  dismissedRef.current = true;
269
259
  onDismiss?.();
@@ -272,11 +262,51 @@ const BottomSheetModalStyled = forwardRef<
272
262
  const handleChange = useCallback(
273
263
  (...args: Parameters<NonNullable<BottomSheetModalProps['onChange']>>) => {
274
264
  dismissedRef.current = false;
265
+ setPresented(args[0] >= 0);
266
+ if (args[0] >= 0) markSheetMounted();
275
267
  onChange?.(...args);
276
268
  },
277
- [onChange],
269
+ [markSheetMounted, onChange],
278
270
  );
279
271
 
272
+ const handleAnimate = useCallback(
273
+ (...args: Parameters<NonNullable<BottomSheetModalProps['onAnimate']>>) => {
274
+ if (args[1] >= 0) markSheetMounted();
275
+ onAnimate?.(...args);
276
+ },
277
+ [markSheetMounted, onAnimate],
278
+ );
279
+
280
+ const id = useId();
281
+ const overlayId = `bottom-sheet-${id}`;
282
+ useEscapeKey(presented, dismissSheet, {
283
+ id: overlayId,
284
+ groupId: overlayId,
285
+ layer: 'modal',
286
+ });
287
+ const { isTopmost } = useOverlayStackRegistration({
288
+ active: presented && Platform.OS !== 'web',
289
+ capabilities: ['back'],
290
+ id: overlayId,
291
+ groupId: overlayId,
292
+ layer: 'modal',
293
+ });
294
+
295
+ useEffect(() => {
296
+ // BackHandler is native-only; react-native-web warns on every subscription.
297
+ if (!presented || Platform.OS === 'web') return;
298
+
299
+ const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
300
+ if (!isTopmost('back')) return false;
301
+ dismissSheet();
302
+ return true;
303
+ });
304
+
305
+ return () => {
306
+ backHandler.remove();
307
+ };
308
+ }, [dismissSheet, isTopmost, presented]);
309
+
280
310
  const sheetContainerStyle = useResolveClassNames(bottomSheetModalContainerClassName);
281
311
  const handleIndicatorStyle = useResolveClassNames(
282
312
  'bg-surface-neutral-subtle w-9 h-[5px] rounded-full',
@@ -284,7 +314,7 @@ const BottomSheetModalStyled = forwardRef<
284
314
 
285
315
  return (
286
316
  <BottomSheetModal
287
- ref={ref}
317
+ ref={setModalRef}
288
318
  enableDynamicSizing
289
319
  maxDynamicContentSize={MAX_DYNAMIC_CONTENT_SIZE}
290
320
  style={sheetContainerStyle}
@@ -295,6 +325,7 @@ const BottomSheetModalStyled = forwardRef<
295
325
  {...rest}
296
326
  onDismiss={handleDismiss}
297
327
  onChange={handleChange}
328
+ onAnimate={handleAnimate}
298
329
  {...(footer != null && { footerComponent: renderFooter })}
299
330
  />
300
331
  );
@@ -1,4 +1,4 @@
1
- import { cva, type VariantProps } from '@cdx-ui/utils';
1
+ import { cva } from '@cdx-ui/utils';
2
2
 
3
3
  /** Keep gorhom's animated container unclipped so web transforms can settle correctly. */
4
4
  export const bottomSheetModalContainerClassName = 'rounded-t-4xl';
@@ -6,27 +6,6 @@ export const bottomSheetModalContainerClassName = 'rounded-t-4xl';
6
6
  /** Clip the static background for rounded corners without clipping the animated sheet. */
7
7
  export const bottomSheetBackgroundClassName = `${bottomSheetModalContainerClassName} overflow-hidden bg-surface-primary`;
8
8
 
9
- export const bottomSheetHeaderVariants = cva(
10
- [
11
- 'flex-row items-center justify-between min-h-11 px-4 pt-3 pb-2.5',
12
- 'bg-surface-primary',
13
- 'text-content-primary',
14
- ],
15
- {
16
- variants: {
17
- hasTitle: {
18
- true: '',
19
- false: 'justify-end',
20
- },
21
- },
22
- defaultVariants: {
23
- hasTitle: true,
24
- },
25
- },
26
- );
27
-
28
9
  export const bottomSheetContentVariants = cva(['bg-surface-primary', 'px-5 pb-5 gap-3']);
29
10
 
30
11
  export const bottomSheetFooterVariants = cva(['px-5 pb-5 gap-2', 'bg-surface-primary']);
31
-
32
- export type BottomSheetHeaderVariants = VariantProps<typeof bottomSheetHeaderVariants>;
@@ -342,7 +342,10 @@ export interface ButtonSpinnerProps extends ActivityIndicatorProps {
342
342
  asChild?: boolean;
343
343
  /** Additional Uniwind/Tailwind classes for the spinner container. */
344
344
  className?: string;
345
- /** Uniwind `accent-*` color class override for the spinner. */
345
+ /**
346
+ * Uniwind `accent-*` color class override for the spinner.
347
+ * When omitted, the color is derived from the parent button's `variant` and `color`.
348
+ */
346
349
  colorClassName?: string;
347
350
  /**
348
351
  * Position hint that emits `data-icon="inline-start"` or `data-icon="inline-end"`.
@@ -350,6 +353,11 @@ export interface ButtonSpinnerProps extends ActivityIndicatorProps {
350
353
  * Omit to apply the symmetric baseline gap.
351
354
  */
352
355
  position?: IconSlotPosition;
356
+ /**
357
+ * Screen reader label for the spinner.
358
+ * @default 'Loading'
359
+ */
360
+ accessibilityLabel?: string;
353
361
  }
354
362
 
355
363
  const ButtonSpinner = forwardRef<ActivityIndicator, ButtonSpinnerProps>(
@@ -22,6 +22,11 @@ const SCOPE = 'CARD';
22
22
  const useCardStyleContext = () => useStyleContext(SCOPE) as CardVariantProps;
23
23
 
24
24
  export interface CardProps extends ViewProps, CardVariantProps {
25
+ /**
26
+ * When `true`, `Card.Content` drops its padding and `Card.Footer` uses standalone
27
+ * padding. Both inherit this unless they set their own `fullBleed`.
28
+ */
29
+ fullBleed?: CardVariantProps['fullBleed'];
25
30
  asChild?: boolean;
26
31
  className?: string;
27
32
  children?: ReactNode;
@@ -117,6 +122,10 @@ CardHeader.displayName = 'Card.Header';
117
122
  // =============================================================================
118
123
 
119
124
  export interface CardContentProps extends ViewProps, CardContentVariantProps {
125
+ /**
126
+ * Removes this region's padding. Inherits the parent `Card`'s `fullBleed` when omitted.
127
+ */
128
+ fullBleed?: CardContentVariantProps['fullBleed'];
120
129
  asChild?: boolean;
121
130
  className?: string;
122
131
  children?: ReactNode;
@@ -152,6 +161,11 @@ CardContent.displayName = 'Card.Content';
152
161
  // =============================================================================
153
162
 
154
163
  export interface CardFooterProps extends ViewProps, CardFooterVariantProps {
164
+ /**
165
+ * Uses standalone padding instead of flush-top padding. Inherits the parent
166
+ * `Card`'s `fullBleed` when omitted.
167
+ */
168
+ fullBleed?: CardFooterVariantProps['fullBleed'];
155
169
  asChild?: boolean;
156
170
  className?: string;
157
171
  children?: ReactNode;
@@ -42,7 +42,7 @@ let hasWarnedChipDefaultColor = false;
42
42
 
43
43
  export interface ChipProps extends IChipProps, Omit<ChipVariantProps, 'variant' | 'color'> {
44
44
  /**
45
- * Visual style variant.
45
+ * Filled subtle surface (`subtle`) or transparent with a border (`outline`).
46
46
  * @default 'subtle'
47
47
  */
48
48
  variant?:
@@ -40,9 +40,9 @@ export interface CurrencyInputProps
40
40
  TextInputProps,
41
41
  'accessibilityLabel' | 'accessibilityHint' | 'testID' | 'onFocus' | 'onBlur' | 'returnKeyType'
42
42
  > {
43
- /** Current numeric value. Renders formatted zero when undefined or null. */
43
+ /** Current amount in major currency units, not cents. Renders a formatted zero when omitted or null. */
44
44
  value?: number;
45
- /** Called with the parsed number when the user changes the amount. */
45
+ /** Called with the parsed amount in major currency units, not cents, when the user edits the field. */
46
46
  onChangeValue?: (value: number) => void;
47
47
  /** Currency symbol rendered as an inline prefix. Defaults to `"$"`. */
48
48
  currencySymbol?: string;
@@ -95,6 +95,7 @@ const AnimatedOverlay = Animated.createAnimatedComponent(DialogOverlayPrimitive)
95
95
  export interface DialogRootProps
96
96
  extends Omit<ViewProps, 'children' | 'role'>, IDialogRootProps, DialogVariantProps {
97
97
  className?: string;
98
+ /** Pass `'disable-all'` to turn off enter/exit and swipe animations for the popup and overlay. */
98
99
  animation?: AnimationRootDisableAll;
99
100
  }
100
101
 
@@ -174,7 +175,16 @@ export interface DialogPopupProps
174
175
  extends Omit<IDialogContentProps, 'asChild'>, Pick<DialogVariantProps, 'size'> {
175
176
  className?: string;
176
177
  children?: ReactNode;
178
+ /**
179
+ * On iOS, render in a plain `View` instead of `FullWindowOverlay`. Re-enables
180
+ * the element inspector, but the popup no longer layers above native modals.
181
+ * @default false
182
+ */
177
183
  disableFullWindowOverlay?: boolean;
184
+ /**
185
+ * Allow swipe-down to dismiss. Native only; no effect on web.
186
+ * @default true
187
+ */
178
188
  isSwipeable?: boolean;
179
189
  /**
180
190
  * Stacking order for the portal overlay layer (backdrop + popup panel).
@@ -141,6 +141,7 @@ export const dialogCloseIconVariants = cva(['size-5 text-content-tertiary']);
141
141
  // ── Exported types ──────────────────────────────────────────
142
142
 
143
143
  export type DialogVariantProps = VariantProps<typeof dialogPopupVariants> & {
144
+ /** Removes horizontal padding from `Dialog.Content`. @default false */
144
145
  fullBleed?: boolean;
145
146
  };
146
147
 
@@ -15,10 +15,15 @@ export interface DividerProps extends ViewProps {
15
15
  className?: string;
16
16
  /** Renders a vertical divider instead of the default horizontal orientation. */
17
17
  vertical?: boolean;
18
+ /**
19
+ * When present, splits the divider into line, content, line.
20
+ * Use `Divider.Label` so the label receives the correct typography and accessibility treatment.
21
+ */
18
22
  children?: ReactNode;
19
23
  }
20
24
 
21
25
  export interface DividerLabelProps extends TextProps {
26
+ /** Renders the consumer's child element in place of the default `Text` host. */
22
27
  asChild?: boolean;
23
28
  className?: string;
24
29
  }
@@ -1,3 +1,3 @@
1
1
  import { cva } from '@cdx-ui/utils';
2
2
 
3
- export const formRootVariants = cva(['flex-col gap-4']);
3
+ export const formRootVariants = cva(['flex flex-col gap-4']);
@@ -0,0 +1,22 @@
1
+ import { Easing, Keyframe } from 'react-native-reanimated';
2
+
3
+ export const FULL_SCREEN_DIALOG_ENTER_MS = 300;
4
+ export const FULL_SCREEN_DIALOG_EXIT_MS = 220;
5
+ export const WEB_TRANSITION_SAFETY_MS = FULL_SCREEN_DIALOG_ENTER_MS + 100;
6
+
7
+ const ENTER_EASING = Easing.bezier(0.32, 0.72, 0, 1);
8
+ const EXIT_EASING = Easing.bezier(0.4, 0, 1, 1);
9
+
10
+ /** Native slide-up surface motion. `offscreen` is the distance below the viewport to start from. */
11
+ export function getFullScreenDialogSurfaceMotion(offscreen: number) {
12
+ return {
13
+ entering: new Keyframe({
14
+ 0: { transform: [{ translateY: offscreen }] },
15
+ 100: { transform: [{ translateY: 0 }], easing: ENTER_EASING },
16
+ }).duration(FULL_SCREEN_DIALOG_ENTER_MS),
17
+ exiting: new Keyframe({
18
+ 0: { transform: [{ translateY: 0 }] },
19
+ 100: { transform: [{ translateY: offscreen }], easing: EXIT_EASING },
20
+ }).duration(FULL_SCREEN_DIALOG_EXIT_MS),
21
+ };
22
+ }