@yahoo/uds-mobile 2.10.0 → 2.12.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 (197) hide show
  1. package/README.md +22 -24
  2. package/dist/bin/fixtures/dist/index.mjs +0 -10
  3. package/dist/bin/modes/dist/index.mjs +1 -0
  4. package/dist/bin/palette/dist/index.mjs +1 -0
  5. package/dist/components/AndroidBackHandler.js.map +1 -1
  6. package/dist/components/Avatar.cjs +1 -1
  7. package/dist/components/Avatar.d.cts +1 -1
  8. package/dist/components/Avatar.d.ts +1 -1
  9. package/dist/components/Avatar.js +1 -1
  10. package/dist/components/Avatar.js.map +1 -1
  11. package/dist/components/Badge.cjs +1 -1
  12. package/dist/components/Badge.d.cts +1 -1
  13. package/dist/components/Badge.d.ts +1 -1
  14. package/dist/components/Badge.js +1 -1
  15. package/dist/components/Badge.js.map +1 -1
  16. package/dist/components/Banner/Banner.js.map +1 -1
  17. package/dist/components/Banner/BannerContent.js.map +1 -1
  18. package/dist/components/Banner/BannerDescription.js.map +1 -1
  19. package/dist/components/Banner/BannerTitle.js.map +1 -1
  20. package/dist/components/Banner/utils.js.map +1 -1
  21. package/dist/components/BlurTarget.cjs +2 -1
  22. package/dist/components/BlurTarget.d.cts +2 -1
  23. package/dist/components/BlurTarget.d.cts.map +1 -1
  24. package/dist/components/BlurTarget.d.ts +2 -1
  25. package/dist/components/BlurTarget.d.ts.map +1 -1
  26. package/dist/components/BlurTarget.js +2 -1
  27. package/dist/components/BlurTarget.js.map +1 -1
  28. package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
  29. package/dist/components/BottomSheet/BottomSheetContent.js.map +1 -1
  30. package/dist/components/BottomSheet/BottomSheetDismiss.js.map +1 -1
  31. package/dist/components/BottomSheet/BottomSheetHandle.js.map +1 -1
  32. package/dist/components/BottomSheet/BottomSheetHeader.js.map +1 -1
  33. package/dist/components/BottomSheet/BottomSheetInternalProvider.js.map +1 -1
  34. package/dist/components/BottomSheet/BottomSheetProvider.js.map +1 -1
  35. package/dist/components/BottomSheet/BottomSheetTrigger.js.map +1 -1
  36. package/dist/components/BottomSheet/useBottomSheetDrag.js.map +1 -1
  37. package/dist/components/BottomSheet/useBottomSheetScroll.js.map +1 -1
  38. package/dist/components/BottomSheet/useBottomSheetSnapModel.js.map +1 -1
  39. package/dist/components/BottomSheet/useBottomSheetStore.js.map +1 -1
  40. package/dist/components/BottomSheet/useExpansionMargins.js.map +1 -1
  41. package/dist/components/BottomSheet/useKeyboardAvoidance.js.map +1 -1
  42. package/dist/components/BottomSheet/utils.js.map +1 -1
  43. package/dist/components/Box.cjs +1 -1
  44. package/dist/components/Box.d.cts +1 -1
  45. package/dist/components/Box.d.ts +1 -1
  46. package/dist/components/Box.js +1 -1
  47. package/dist/components/Box.js.map +1 -1
  48. package/dist/components/Button.cjs +1 -1
  49. package/dist/components/Button.d.cts +1 -1
  50. package/dist/components/Button.d.ts +1 -1
  51. package/dist/components/Button.js +1 -1
  52. package/dist/components/Button.js.map +1 -1
  53. package/dist/components/Checkbox.cjs +1 -1
  54. package/dist/components/Checkbox.d.cts +1 -1
  55. package/dist/components/Checkbox.d.ts +1 -1
  56. package/dist/components/Checkbox.js +1 -1
  57. package/dist/components/Checkbox.js.map +1 -1
  58. package/dist/components/Chip.cjs +1 -1
  59. package/dist/components/Chip.d.cts +1 -1
  60. package/dist/components/Chip.d.ts +1 -1
  61. package/dist/components/Chip.js +1 -1
  62. package/dist/components/Chip.js.map +1 -1
  63. package/dist/components/Divider/Divider.cjs +103 -0
  64. package/dist/components/Divider/Divider.d.cts +50 -0
  65. package/dist/components/Divider/Divider.d.cts.map +1 -0
  66. package/dist/components/Divider/Divider.d.ts +50 -0
  67. package/dist/components/Divider/Divider.d.ts.map +1 -0
  68. package/dist/components/Divider/Divider.js +103 -0
  69. package/dist/components/Divider/Divider.js.map +1 -0
  70. package/dist/components/Divider/DividerLabel.cjs +37 -0
  71. package/dist/components/Divider/DividerLabel.d.cts +18 -0
  72. package/dist/components/Divider/DividerLabel.d.cts.map +1 -0
  73. package/dist/components/Divider/DividerLabel.d.ts +18 -0
  74. package/dist/components/Divider/DividerLabel.d.ts.map +1 -0
  75. package/dist/components/Divider/DividerLabel.js +37 -0
  76. package/dist/components/Divider/DividerLabel.js.map +1 -0
  77. package/dist/components/Divider/DividerLine.cjs +62 -0
  78. package/dist/components/Divider/DividerLine.d.cts +19 -0
  79. package/dist/components/Divider/DividerLine.d.cts.map +1 -0
  80. package/dist/components/Divider/DividerLine.d.ts +19 -0
  81. package/dist/components/Divider/DividerLine.d.ts.map +1 -0
  82. package/dist/components/Divider/DividerLine.js +62 -0
  83. package/dist/components/Divider/DividerLine.js.map +1 -0
  84. package/dist/components/Divider/index.cjs +8 -0
  85. package/dist/components/Divider/index.d.cts +6 -0
  86. package/dist/components/Divider/index.d.ts +6 -0
  87. package/dist/components/Divider/index.js +5 -0
  88. package/dist/components/Divider/types.cjs +1 -0
  89. package/dist/components/Divider/types.d.cts +35 -0
  90. package/dist/components/Divider/types.d.cts.map +1 -0
  91. package/dist/components/Divider/types.d.ts +35 -0
  92. package/dist/components/Divider/types.d.ts.map +1 -0
  93. package/dist/components/Divider/types.js +1 -0
  94. package/dist/components/Divider/utils.cjs +33 -0
  95. package/dist/components/Divider/utils.d.cts +12 -0
  96. package/dist/components/Divider/utils.d.cts.map +1 -0
  97. package/dist/components/Divider/utils.d.ts +12 -0
  98. package/dist/components/Divider/utils.d.ts.map +1 -0
  99. package/dist/components/Divider/utils.js +31 -0
  100. package/dist/components/Divider/utils.js.map +1 -0
  101. package/dist/components/HStack.cjs +1 -1
  102. package/dist/components/HStack.d.cts +1 -1
  103. package/dist/components/HStack.d.ts +1 -1
  104. package/dist/components/HStack.js +1 -1
  105. package/dist/components/HStack.js.map +1 -1
  106. package/dist/components/Icon.cjs +1 -1
  107. package/dist/components/Icon.d.cts +2 -2
  108. package/dist/components/Icon.d.ts +2 -2
  109. package/dist/components/Icon.js +1 -1
  110. package/dist/components/Icon.js.map +1 -1
  111. package/dist/components/IconButton.cjs +1 -1
  112. package/dist/components/IconButton.d.cts +1 -1
  113. package/dist/components/IconButton.d.ts +1 -1
  114. package/dist/components/IconButton.js +1 -1
  115. package/dist/components/IconButton.js.map +1 -1
  116. package/dist/components/IconSlot.cjs +1 -1
  117. package/dist/components/IconSlot.d.cts +1 -1
  118. package/dist/components/IconSlot.d.ts +1 -1
  119. package/dist/components/IconSlot.js +1 -1
  120. package/dist/components/IconSlot.js.map +1 -1
  121. package/dist/components/Image.cjs +1 -1
  122. package/dist/components/Image.d.cts +1 -1
  123. package/dist/components/Image.d.ts +1 -1
  124. package/dist/components/Image.js +1 -1
  125. package/dist/components/Image.js.map +1 -1
  126. package/dist/components/Input.cjs +1 -1
  127. package/dist/components/Input.d.cts +1 -1
  128. package/dist/components/Input.d.ts +1 -1
  129. package/dist/components/Input.js +1 -1
  130. package/dist/components/Input.js.map +1 -1
  131. package/dist/components/Link.cjs +1 -1
  132. package/dist/components/Link.d.cts +1 -1
  133. package/dist/components/Link.d.ts +1 -1
  134. package/dist/components/Link.js +1 -1
  135. package/dist/components/Link.js.map +1 -1
  136. package/dist/components/Pressable.cjs +1 -1
  137. package/dist/components/Pressable.d.cts +1 -1
  138. package/dist/components/Pressable.d.ts +1 -1
  139. package/dist/components/Pressable.js +1 -1
  140. package/dist/components/Pressable.js.map +1 -1
  141. package/dist/components/Radio.cjs +1 -1
  142. package/dist/components/Radio.d.cts +1 -1
  143. package/dist/components/Radio.d.ts +1 -1
  144. package/dist/components/Radio.js +1 -1
  145. package/dist/components/Radio.js.map +1 -1
  146. package/dist/components/Screen.cjs +1 -1
  147. package/dist/components/Screen.d.cts +1 -1
  148. package/dist/components/Screen.d.ts +1 -1
  149. package/dist/components/Screen.js +1 -1
  150. package/dist/components/Screen.js.map +1 -1
  151. package/dist/components/Scrim.js.map +1 -1
  152. package/dist/components/Switch.cjs +1 -1
  153. package/dist/components/Switch.d.cts +1 -1
  154. package/dist/components/Switch.d.ts +1 -1
  155. package/dist/components/Switch.js +1 -1
  156. package/dist/components/Switch.js.map +1 -1
  157. package/dist/components/Tabs/Tab.js.map +1 -1
  158. package/dist/components/Tabs/TabList.js.map +1 -1
  159. package/dist/components/Tabs/TabPanel.js.map +1 -1
  160. package/dist/components/Tabs/Tabs.js.map +1 -1
  161. package/dist/components/Tabs/tabTheme.js.map +1 -1
  162. package/dist/components/Tabs/tabsContexts.js.map +1 -1
  163. package/dist/components/Text.cjs +1 -1
  164. package/dist/components/Text.d.cts +1 -1
  165. package/dist/components/Text.d.ts +1 -1
  166. package/dist/components/Text.js +1 -1
  167. package/dist/components/Text.js.map +1 -1
  168. package/dist/components/UDSProvider.js.map +1 -1
  169. package/dist/components/VStack.cjs +1 -1
  170. package/dist/components/VStack.d.cts +1 -1
  171. package/dist/components/VStack.d.ts +1 -1
  172. package/dist/components/VStack.js +1 -1
  173. package/dist/components/VStack.js.map +1 -1
  174. package/dist/jest/mocks/icons.js.map +1 -1
  175. package/dist/jest/mocks/react-native.js.map +1 -1
  176. package/dist/jest/mocks/reanimated.js.map +1 -1
  177. package/dist/jest/mocks/styles.cjs +15 -0
  178. package/dist/jest/mocks/styles.d.cts +3 -2
  179. package/dist/jest/mocks/styles.d.cts.map +1 -1
  180. package/dist/jest/mocks/styles.d.ts +3 -2
  181. package/dist/jest/mocks/styles.d.ts.map +1 -1
  182. package/dist/jest/mocks/styles.js +15 -1
  183. package/dist/jest/mocks/styles.js.map +1 -1
  184. package/dist/jest/mocks/svg.js.map +1 -1
  185. package/dist/jest/mocks/unistyles.js.map +1 -1
  186. package/dist/jest/setup.js.map +1 -1
  187. package/dist/portal.js.map +1 -1
  188. package/dist/types/dist/index.d.cts +12 -2
  189. package/dist/types/dist/index.d.cts.map +1 -1
  190. package/dist/types/dist/index.d.ts +12 -2
  191. package/dist/types/dist/index.d.ts.map +1 -1
  192. package/fonts/uds-icons.ttf +0 -0
  193. package/generated/styles.cjs +19 -13
  194. package/generated/styles.d.ts +12 -0
  195. package/generated/styles.mjs +19 -13
  196. package/generated/unistyles.d.ts +9 -9
  197. package/package.json +11 -1
package/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  `@yahoo/uds-mobile` brings UDS to React Native. It provides:
19
19
 
20
- - **Pre-built Components**: Avatar, Badge, Button, Checkbox, Chip, Icon, IconButton, Image, Input, Link, Radio, Switch, Tabs, Text, and layout primitives (Box, VStack, HStack, Screen)
20
+ - **Pre-built Components**: Avatar, Badge, Button, Checkbox, Chip, Divider, Icon, IconButton, Image, Input, Link, Radio, Switch, Tabs, Text, and layout primitives (Box, VStack, HStack, Screen)
21
21
  - **Theming**: Full light/dark mode support with automatic system preference detection
22
22
  - **Design Token Integration**: Colors, typography, spacing, and motion configs synced from UDS tokens
23
23
  - **Animations**: Smooth, physics-based animations using Reanimated with motion parity to web
@@ -327,6 +327,7 @@ const styles = StyleSheet.create((theme) => ({
327
327
  | `Button` | Interactive button with variants and animations |
328
328
  | `Checkbox` | Selectable checkbox with label |
329
329
  | `Chip` | Compact elements for filters and selections |
330
+ | `Divider` | Visual separator for grouping content |
330
331
  | `HStack` | Horizontal flex container |
331
332
  | `Icon` | Icon rendering (font or SVG) |
332
333
  | `IconButton` | Icon-only button |
@@ -656,29 +657,25 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) for the development workflow, architect
656
657
  All components export their props types:
657
658
 
658
659
  ```typescript
659
- import type {
660
- ButtonProps,
661
- TextProps,
662
- AvatarProps,
663
- BadgeProps,
664
- CheckboxProps,
665
- ChipProps,
666
- IconProps,
667
- IconButtonProps,
668
- ImageProps,
669
- InputProps,
670
- LinkProps,
671
- RadioProps,
672
- SwitchProps,
673
- BoxProps,
674
- VStackProps,
675
- HStackProps,
676
- ScreenProps,
677
- PressableProps,
678
- IconSlotProps,
679
- IconSlotType,
680
- IconName,
681
- } from '@yahoo/uds-mobile';
660
+ import type { ButtonProps } from '@yahoo/uds-mobile/Button';
661
+ import type { TextProps } from '@yahoo/uds-mobile/Text';
662
+ import type { AvatarProps } from '@yahoo/uds-mobile/Avatar';
663
+ import type { BadgeProps } from '@yahoo/uds-mobile/Badge';
664
+ import type { CheckboxProps } from '@yahoo/uds-mobile/Checkbox';
665
+ import type { ChipProps } from '@yahoo/uds-mobile/Chip';
666
+ import type { IconProps, IconName } from '@yahoo/uds-mobile/Icon';
667
+ import type { IconButtonProps } from '@yahoo/uds-mobile/IconButton';
668
+ import type { ImageProps } from '@yahoo/uds-mobile/Image';
669
+ import type { InputProps } from '@yahoo/uds-mobile/Input';
670
+ import type { LinkProps } from '@yahoo/uds-mobile/Link';
671
+ import type { RadioProps } from '@yahoo/uds-mobile/Radio';
672
+ import type { SwitchProps } from '@yahoo/uds-mobile/Switch';
673
+ import type { BoxProps } from '@yahoo/uds-mobile/Box';
674
+ import type { VStackProps } from '@yahoo/uds-mobile/VStack';
675
+ import type { HStackProps } from '@yahoo/uds-mobile/HStack';
676
+ import type { ScreenProps } from '@yahoo/uds-mobile/Screen';
677
+ import type { PressableProps } from '@yahoo/uds-mobile/Pressable';
678
+ import type { IconSlotProps, IconSlotType } from '@yahoo/uds-mobile/IconSlot';
682
679
  ```
683
680
 
684
681
  ### Exports
@@ -691,6 +688,7 @@ import { Box } from '@yahoo/uds-mobile/Box';
691
688
  import { Button } from '@yahoo/uds-mobile/Button';
692
689
  import { Checkbox } from '@yahoo/uds-mobile/Checkbox';
693
690
  import { Chip } from '@yahoo/uds-mobile/Chip';
691
+ import { Divider, DividerLabel, DividerLine } from '@yahoo/uds-mobile/Divider';
694
692
  import { HStack } from '@yahoo/uds-mobile/HStack';
695
693
  import { Icon, iconNames, multicolorIconNames } from '@yahoo/uds-mobile/Icon';
696
694
  import { IconButton } from '@yahoo/uds-mobile/IconButton';
@@ -1,14 +1,4 @@
1
1
  #!/usr/bin/env node
2
- //#region ../fixtures/dist/index.js
3
- function createArbitraryFixture(fixture) {
4
- return fixture;
5
- }
6
- createArbitraryFixture({
7
- type: "arbitraryFixture",
8
- valueType: "integer",
9
- min: 0,
10
- step: 1
11
- });
12
2
  var elevationAliases = [
13
3
  "elevation-0",
14
4
  "elevation-1",
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  //#region ../modes/dist/index.js
3
+ /*! © 2026 Yahoo, Inc. UDS Modes v0.0.0-development */
3
4
  const DEFAULT_SCALE_MODE = "large";
4
5
  //#endregion
5
6
  export { DEFAULT_SCALE_MODE };
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  //#region ../palette/dist/index.js
3
+ /*! © 2026 Yahoo, Inc. UDS Palette v0.0.0-development */
3
4
  const deprecatedAlwaysPalette = {
4
5
  /**
5
6
  * @deprecated Use 'always/white' instead
@@ -1 +1 @@
1
- {"version":3,"file":"AndroidBackHandler.js","names":[],"sources":["../../src/components/AndroidBackHandler.tsx"],"sourcesContent":["import { useEffect } from 'react';\nimport { BackHandler } from 'react-native';\n\ninterface AndroidBackHandlerProps {\n isOpen: boolean;\n dismissible: boolean;\n onDismiss: () => void;\n}\n\n/**\n * Handles the Android hardware back button.\n *\n * While `isOpen` is true, intercepts the back press. If `dismissible`, calls\n * `onDismiss`; otherwise swallows the event to prevent navigation.\n */\nfunction AndroidBackHandler({ isOpen, dismissible, onDismiss }: AndroidBackHandlerProps) {\n useEffect(() => {\n if (!isOpen) {\n return;\n }\n\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n if (dismissible) {\n onDismiss();\n }\n return true;\n });\n\n return () => subscription.remove();\n }, [isOpen, dismissible, onDismiss]);\n\n return null;\n}\n\nexport { AndroidBackHandler };\nexport type { AndroidBackHandlerProps };\n"],"mappings":";;;;;;;;;;AAeA,SAAS,mBAAmB,EAAE,QAAQ,aAAa,aAAsC;AACvF,iBAAgB;AACd,MAAI,CAAC,OACH;EAGF,MAAM,eAAe,YAAY,iBAAiB,2BAA2B;AAC3E,OAAI,YACF,YAAW;AAEb,UAAO;IACP;AAEF,eAAa,aAAa,QAAQ;IACjC;EAAC;EAAQ;EAAa;EAAU,CAAC;AAEpC,QAAO"}
1
+ {"version":3,"file":"AndroidBackHandler.js","names":[],"sources":["../../src/components/AndroidBackHandler.tsx"],"sourcesContent":["import { useEffect } from 'react';\nimport { BackHandler } from 'react-native';\n\ninterface AndroidBackHandlerProps {\n isOpen: boolean;\n dismissible: boolean;\n onDismiss: () => void;\n}\n\n/**\n * Handles the Android hardware back button.\n *\n * While `isOpen` is true, intercepts the back press. If `dismissible`, calls\n * `onDismiss`; otherwise swallows the event to prevent navigation.\n */\nfunction AndroidBackHandler({ isOpen, dismissible, onDismiss }: AndroidBackHandlerProps) {\n useEffect(() => {\n if (!isOpen) {\n return;\n }\n\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n if (dismissible) {\n onDismiss();\n }\n return true;\n });\n\n return () => subscription.remove();\n }, [isOpen, dismissible, onDismiss]);\n\n return null;\n}\n\nexport { AndroidBackHandler };\nexport type { AndroidBackHandlerProps };\n"],"mappings":";;;;;;;;;;AAeA,SAAS,mBAAmB,EAAE,QAAQ,aAAa,aAAsC;CACvF,gBAAgB;EACd,IAAI,CAAC,QACH;EAGF,MAAM,eAAe,YAAY,iBAAiB,2BAA2B;GAC3E,IAAI,aACF,WAAW;GAEb,OAAO;IACP;EAEF,aAAa,aAAa,QAAQ;IACjC;EAAC;EAAQ;EAAa;EAAU,CAAC;CAEpC,OAAO"}
@@ -35,7 +35,7 @@ function generateInitials(name, strategy = "firstAndLast") {
35
35
  *
36
36
  * @example
37
37
  * ```tsx
38
- * import { Avatar } from '@yahoo/uds-mobile';
38
+ * import { Avatar } from '@yahoo/uds-mobile/Avatar';
39
39
  *
40
40
  * // With image
41
41
  * <Avatar
@@ -37,7 +37,7 @@ interface AvatarProps extends ViewProps {
37
37
  *
38
38
  * @example
39
39
  * ```tsx
40
- * import { Avatar } from '@yahoo/uds-mobile';
40
+ * import { Avatar } from '@yahoo/uds-mobile/Avatar';
41
41
  *
42
42
  * // With image
43
43
  * <Avatar
@@ -37,7 +37,7 @@ interface AvatarProps extends ViewProps {
37
37
  *
38
38
  * @example
39
39
  * ```tsx
40
- * import { Avatar } from '@yahoo/uds-mobile';
40
+ * import { Avatar } from '@yahoo/uds-mobile/Avatar';
41
41
  *
42
42
  * // With image
43
43
  * <Avatar
@@ -33,7 +33,7 @@ function generateInitials(name, strategy = "firstAndLast") {
33
33
  *
34
34
  * @example
35
35
  * ```tsx
36
- * import { Avatar } from '@yahoo/uds-mobile';
36
+ * import { Avatar } from '@yahoo/uds-mobile/Avatar';
37
37
  *
38
38
  * // With image
39
39
  * <Avatar
@@ -1 +1 @@
1
- {"version":3,"file":"Avatar.js","names":["RNImage","Text"],"sources":["../../src/components/Avatar.tsx"],"sourcesContent":["import { forwardRef, useState } from 'react';\nimport type { View, ViewProps } from 'react-native';\nimport { Image as RNImage } from 'react-native';\n\nimport { avatarStyles } from '../../generated/styles';\nimport { Box } from './Box';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport { Text } from './Text';\n\n/** Avatar size options */\ntype AvatarSize = '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';\n\n/** Avatar variant options */\ntype AvatarVariant = 'primary' | 'secondary';\n\n/** Abbreviation strategy for generating initials */\ntype AbbreviationStrategy =\n | 'first'\n | 'last'\n | 'firstAndLast'\n | 'firstTwo'\n | 'firstThree'\n | 'firstOfEach'\n | ((name: string) => string);\n\ninterface AvatarProps extends ViewProps {\n /** Image source URL */\n src?: string;\n /** Alt text for the image, also used for generating initials */\n alt?: string;\n /** Explicit initials to display (overrides auto-generated from alt) */\n fallback?: string;\n /** Size of the avatar @default 'md' */\n size?: AvatarSize;\n /** Variant style @default 'primary' */\n variant?: AvatarVariant;\n /** Custom icon to display when no image or text fallback */\n icon?: IconSlotType;\n /** Strategy for generating initials from name @default 'firstAndLast' */\n abbreviationStrategy?: AbbreviationStrategy;\n}\n\nconst abbreviationStrategies: Record<\n Exclude<AbbreviationStrategy, (name: string) => string>,\n (initials: string[]) => string\n> = {\n first: (initials) => initials[0] ?? '',\n last: (initials) => initials[initials.length - 1] ?? '',\n firstAndLast: (initials) =>\n initials.length === 1 ? (initials[0] ?? '') : `${initials[0]}${initials[initials.length - 1]}`,\n firstTwo: (initials) => initials.slice(0, 2).join(''),\n firstThree: (initials) => initials.slice(0, 3).join(''),\n firstOfEach: (initials) => initials.join(''),\n};\n\n/** Generate initials from a name */\nfunction generateInitials(name?: string, strategy: AbbreviationStrategy = 'firstAndLast'): string {\n if (!name) {\n return '';\n }\n\n if (typeof strategy === 'function') {\n return strategy(name);\n }\n\n const words = name.trim().split(/\\s+/);\n const initials = words.map((word) => word[0]?.toUpperCase() ?? '');\n\n return abbreviationStrategies[strategy](initials);\n}\n\n/**\n * **Avatar component for user representation**\n *\n * @description\n * Displays a user avatar with image, initials fallback, or icon fallback.\n *\n * @category Display\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Avatar } from '@yahoo/uds-mobile';\n *\n * // With image\n * <Avatar\n * src=\"https://example.com/photo.jpg\"\n * alt=\"Jane Doe\"\n * />\n *\n * // With initials fallback\n * <Avatar alt=\"Jane Doe\" />\n *\n * // With explicit initials\n * <Avatar fallback=\"JD\" />\n *\n * // With custom icon\n * <Avatar icon=\"Person\" variant=\"secondary\" />\n * ```\n *\n * @accessibility\n * - Sets `accessibilityRole=\"image\"` automatically\n * - Uses `alt` prop as accessibility label\n * - Always provide meaningful `alt` text for user identification\n *\n * @see {@link Image} for general image display\n */\nconst Avatar = forwardRef<View, AvatarProps>(function Avatar(\n {\n src,\n alt,\n fallback,\n size = 'md',\n variant = 'primary',\n icon,\n abbreviationStrategy = 'firstAndLast',\n style,\n ...props\n },\n ref,\n) {\n const [imageError, setImageError] = useState(false);\n\n const initials = fallback || generateInitials(alt, abbreviationStrategy);\n\n const hasImage = src && !imageError;\n const hasText = initials.length > 0;\n const showImage = hasImage;\n const showText = !hasImage && hasText;\n const showIcon = !hasImage && !hasText;\n\n avatarStyles.useVariants({\n size,\n ...(showImage && { image: variant }),\n ...(showText && { text: variant }),\n ...(showIcon && { icon: variant }),\n });\n\n return (\n <Box\n ref={ref}\n style={[\n avatarStyles.root,\n { alignItems: 'center', justifyContent: 'center', overflow: 'hidden' },\n style,\n ]}\n accessibilityRole=\"image\"\n accessibilityLabel={alt}\n {...props}\n >\n {showImage && (\n <RNImage\n source={{ uri: src }}\n style={{ width: '100%', height: '100%' }}\n onError={() => setImageError(true)}\n accessibilityLabel={alt}\n />\n )}\n\n {showText && (\n <Text style={avatarStyles.text} numberOfLines={1}>\n {initials}\n </Text>\n )}\n\n {showIcon && <IconSlot icon={icon ?? 'Person'} variant=\"fill\" style={avatarStyles.icon} />}\n </Box>\n );\n});\n\nAvatar.displayName = 'Avatar';\n\nexport { Avatar, type AvatarProps };\n"],"mappings":";;;;;;;;;AA2CA,MAAM,yBAGF;CACF,QAAQ,aAAa,SAAS,MAAM;CACpC,OAAO,aAAa,SAAS,SAAS,SAAS,MAAM;CACrD,eAAe,aACb,SAAS,WAAW,IAAK,SAAS,MAAM,KAAM,GAAG,SAAS,KAAK,SAAS,SAAS,SAAS;CAC5F,WAAW,aAAa,SAAS,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG;CACrD,aAAa,aAAa,SAAS,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG;CACvD,cAAc,aAAa,SAAS,KAAK,GAAG;CAC7C;;AAGD,SAAS,iBAAiB,MAAe,WAAiC,gBAAwB;AAChG,KAAI,CAAC,KACH,QAAO;AAGT,KAAI,OAAO,aAAa,WACtB,QAAO,SAAS,KAAK;CAIvB,MAAM,WADQ,KAAK,MAAM,CAAC,MAAM,MACV,CAAC,KAAK,SAAS,KAAK,IAAI,aAAa,IAAI,GAAG;AAElE,QAAO,uBAAuB,UAAU,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCnD,MAAM,SAAS,WAA8B,SAAS,OACpD,EACE,KACA,KACA,UACA,OAAO,MACP,UAAU,WACV,MACA,uBAAuB,gBACvB,OACA,GAAG,SAEL,KACA;CACA,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CAEnD,MAAM,WAAW,YAAY,iBAAiB,KAAK,qBAAqB;CAExE,MAAM,WAAW,OAAO,CAAC;CACzB,MAAM,UAAU,SAAS,SAAS;CAClC,MAAM,YAAY;CAClB,MAAM,WAAW,CAAC,YAAY;CAC9B,MAAM,WAAW,CAAC,YAAY,CAAC;AAE/B,cAAa,YAAY;EACvB;EACA,GAAI,aAAa,EAAE,OAAO,SAAS;EACnC,GAAI,YAAY,EAAE,MAAM,SAAS;EACjC,GAAI,YAAY,EAAE,MAAM,SAAS;EAClC,CAAC;AAEF,QACE,qBAAC,KAAD;EACO;EACL,OAAO;GACL,aAAa;GACb;IAAE,YAAY;IAAU,gBAAgB;IAAU,UAAU;IAAU;GACtE;GACD;EACD,mBAAkB;EAClB,oBAAoB;EACpB,GAAI;YATN;GAWG,aACC,oBAACA,OAAD;IACE,QAAQ,EAAE,KAAK,KAAK;IACpB,OAAO;KAAE,OAAO;KAAQ,QAAQ;KAAQ;IACxC,eAAe,cAAc,KAAK;IAClC,oBAAoB;IACpB,CAAA;GAGH,YACC,oBAACC,QAAD;IAAM,OAAO,aAAa;IAAM,eAAe;cAC5C;IACI,CAAA;GAGR,YAAY,oBAAC,UAAD;IAAU,MAAM,QAAQ;IAAU,SAAQ;IAAO,OAAO,aAAa;IAAQ,CAAA;GACtF;;EAER;AAEF,OAAO,cAAc"}
1
+ {"version":3,"file":"Avatar.js","names":["RNImage","Text"],"sources":["../../src/components/Avatar.tsx"],"sourcesContent":["import { forwardRef, useState } from 'react';\nimport type { View, ViewProps } from 'react-native';\nimport { Image as RNImage } from 'react-native';\n\nimport { avatarStyles } from '../../generated/styles';\nimport { Box } from './Box';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport { Text } from './Text';\n\n/** Avatar size options */\ntype AvatarSize = '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';\n\n/** Avatar variant options */\ntype AvatarVariant = 'primary' | 'secondary';\n\n/** Abbreviation strategy for generating initials */\ntype AbbreviationStrategy =\n | 'first'\n | 'last'\n | 'firstAndLast'\n | 'firstTwo'\n | 'firstThree'\n | 'firstOfEach'\n | ((name: string) => string);\n\ninterface AvatarProps extends ViewProps {\n /** Image source URL */\n src?: string;\n /** Alt text for the image, also used for generating initials */\n alt?: string;\n /** Explicit initials to display (overrides auto-generated from alt) */\n fallback?: string;\n /** Size of the avatar @default 'md' */\n size?: AvatarSize;\n /** Variant style @default 'primary' */\n variant?: AvatarVariant;\n /** Custom icon to display when no image or text fallback */\n icon?: IconSlotType;\n /** Strategy for generating initials from name @default 'firstAndLast' */\n abbreviationStrategy?: AbbreviationStrategy;\n}\n\nconst abbreviationStrategies: Record<\n Exclude<AbbreviationStrategy, (name: string) => string>,\n (initials: string[]) => string\n> = {\n first: (initials) => initials[0] ?? '',\n last: (initials) => initials[initials.length - 1] ?? '',\n firstAndLast: (initials) =>\n initials.length === 1 ? (initials[0] ?? '') : `${initials[0]}${initials[initials.length - 1]}`,\n firstTwo: (initials) => initials.slice(0, 2).join(''),\n firstThree: (initials) => initials.slice(0, 3).join(''),\n firstOfEach: (initials) => initials.join(''),\n};\n\n/** Generate initials from a name */\nfunction generateInitials(name?: string, strategy: AbbreviationStrategy = 'firstAndLast'): string {\n if (!name) {\n return '';\n }\n\n if (typeof strategy === 'function') {\n return strategy(name);\n }\n\n const words = name.trim().split(/\\s+/);\n const initials = words.map((word) => word[0]?.toUpperCase() ?? '');\n\n return abbreviationStrategies[strategy](initials);\n}\n\n/**\n * **Avatar component for user representation**\n *\n * @description\n * Displays a user avatar with image, initials fallback, or icon fallback.\n *\n * @category Display\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Avatar } from '@yahoo/uds-mobile/Avatar';\n *\n * // With image\n * <Avatar\n * src=\"https://example.com/photo.jpg\"\n * alt=\"Jane Doe\"\n * />\n *\n * // With initials fallback\n * <Avatar alt=\"Jane Doe\" />\n *\n * // With explicit initials\n * <Avatar fallback=\"JD\" />\n *\n * // With custom icon\n * <Avatar icon=\"Person\" variant=\"secondary\" />\n * ```\n *\n * @accessibility\n * - Sets `accessibilityRole=\"image\"` automatically\n * - Uses `alt` prop as accessibility label\n * - Always provide meaningful `alt` text for user identification\n *\n * @see {@link Image} for general image display\n */\nconst Avatar = forwardRef<View, AvatarProps>(function Avatar(\n {\n src,\n alt,\n fallback,\n size = 'md',\n variant = 'primary',\n icon,\n abbreviationStrategy = 'firstAndLast',\n style,\n ...props\n },\n ref,\n) {\n const [imageError, setImageError] = useState(false);\n\n const initials = fallback || generateInitials(alt, abbreviationStrategy);\n\n const hasImage = src && !imageError;\n const hasText = initials.length > 0;\n const showImage = hasImage;\n const showText = !hasImage && hasText;\n const showIcon = !hasImage && !hasText;\n\n avatarStyles.useVariants({\n size,\n ...(showImage && { image: variant }),\n ...(showText && { text: variant }),\n ...(showIcon && { icon: variant }),\n });\n\n return (\n <Box\n ref={ref}\n style={[\n avatarStyles.root,\n { alignItems: 'center', justifyContent: 'center', overflow: 'hidden' },\n style,\n ]}\n accessibilityRole=\"image\"\n accessibilityLabel={alt}\n {...props}\n >\n {showImage && (\n <RNImage\n source={{ uri: src }}\n style={{ width: '100%', height: '100%' }}\n onError={() => setImageError(true)}\n accessibilityLabel={alt}\n />\n )}\n\n {showText && (\n <Text style={avatarStyles.text} numberOfLines={1}>\n {initials}\n </Text>\n )}\n\n {showIcon && <IconSlot icon={icon ?? 'Person'} variant=\"fill\" style={avatarStyles.icon} />}\n </Box>\n );\n});\n\nAvatar.displayName = 'Avatar';\n\nexport { Avatar, type AvatarProps };\n"],"mappings":";;;;;;;;;AA2CA,MAAM,yBAGF;CACF,QAAQ,aAAa,SAAS,MAAM;CACpC,OAAO,aAAa,SAAS,SAAS,SAAS,MAAM;CACrD,eAAe,aACb,SAAS,WAAW,IAAK,SAAS,MAAM,KAAM,GAAG,SAAS,KAAK,SAAS,SAAS,SAAS;CAC5F,WAAW,aAAa,SAAS,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG;CACrD,aAAa,aAAa,SAAS,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG;CACvD,cAAc,aAAa,SAAS,KAAK,GAAG;CAC7C;;AAGD,SAAS,iBAAiB,MAAe,WAAiC,gBAAwB;CAChG,IAAI,CAAC,MACH,OAAO;CAGT,IAAI,OAAO,aAAa,YACtB,OAAO,SAAS,KAAK;CAIvB,MAAM,WADQ,KAAK,MAAM,CAAC,MAAM,MACV,CAAC,KAAK,SAAS,KAAK,IAAI,aAAa,IAAI,GAAG;CAElE,OAAO,uBAAuB,UAAU,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCnD,MAAM,SAAS,WAA8B,SAAS,OACpD,EACE,KACA,KACA,UACA,OAAO,MACP,UAAU,WACV,MACA,uBAAuB,gBACvB,OACA,GAAG,SAEL,KACA;CACA,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CAEnD,MAAM,WAAW,YAAY,iBAAiB,KAAK,qBAAqB;CAExE,MAAM,WAAW,OAAO,CAAC;CACzB,MAAM,UAAU,SAAS,SAAS;CAClC,MAAM,YAAY;CAClB,MAAM,WAAW,CAAC,YAAY;CAC9B,MAAM,WAAW,CAAC,YAAY,CAAC;CAE/B,aAAa,YAAY;EACvB;EACA,GAAI,aAAa,EAAE,OAAO,SAAS;EACnC,GAAI,YAAY,EAAE,MAAM,SAAS;EACjC,GAAI,YAAY,EAAE,MAAM,SAAS;EAClC,CAAC;CAEF,OACE,qBAAC,KAAD;EACO;EACL,OAAO;GACL,aAAa;GACb;IAAE,YAAY;IAAU,gBAAgB;IAAU,UAAU;IAAU;GACtE;GACD;EACD,mBAAkB;EAClB,oBAAoB;EACpB,GAAI;YATN;GAWG,aACC,oBAACA,OAAD;IACE,QAAQ,EAAE,KAAK,KAAK;IACpB,OAAO;KAAE,OAAO;KAAQ,QAAQ;KAAQ;IACxC,eAAe,cAAc,KAAK;IAClC,oBAAoB;IACpB,CAAA;GAGH,YACC,oBAACC,QAAD;IAAM,OAAO,aAAa;IAAM,eAAe;cAC5C;IACI,CAAA;GAGR,YAAY,oBAAC,UAAD;IAAU,MAAM,QAAQ;IAAU,SAAQ;IAAO,OAAO,aAAa;IAAQ,CAAA;GACtF;;EAER;AAEF,OAAO,cAAc"}
@@ -19,7 +19,7 @@ let generated_styles = require("../../generated/styles");
19
19
  *
20
20
  * @example
21
21
  * ```tsx
22
- * import { Badge } from '@yahoo/uds-mobile';
22
+ * import { Badge } from '@yahoo/uds-mobile/Badge';
23
23
  *
24
24
  * <Badge>Label</Badge>
25
25
  * <Badge variant="brand" size="sm">New</Badge>
@@ -41,7 +41,7 @@ interface BadgeProps extends ViewProps {
41
41
  *
42
42
  * @example
43
43
  * ```tsx
44
- * import { Badge } from '@yahoo/uds-mobile';
44
+ * import { Badge } from '@yahoo/uds-mobile/Badge';
45
45
  *
46
46
  * <Badge>Label</Badge>
47
47
  * <Badge variant="brand" size="sm">New</Badge>
@@ -41,7 +41,7 @@ interface BadgeProps extends ViewProps {
41
41
  *
42
42
  * @example
43
43
  * ```tsx
44
- * import { Badge } from '@yahoo/uds-mobile';
44
+ * import { Badge } from '@yahoo/uds-mobile/Badge';
45
45
  *
46
46
  * <Badge>Label</Badge>
47
47
  * <Badge variant="brand" size="sm">New</Badge>
@@ -17,7 +17,7 @@ import { badgeStyles } from "../../generated/styles";
17
17
  *
18
18
  * @example
19
19
  * ```tsx
20
- * import { Badge } from '@yahoo/uds-mobile';
20
+ * import { Badge } from '@yahoo/uds-mobile/Badge';
21
21
  *
22
22
  * <Badge>Label</Badge>
23
23
  * <Badge variant="brand" size="sm">New</Badge>
@@ -1 +1 @@
1
- {"version":3,"file":"Badge.js","names":[],"sources":["../../src/components/Badge.tsx"],"sourcesContent":["import type { BadgeSize, BadgeVariant } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { memo, useMemo } from 'react';\nimport type { View, ViewProps } from 'react-native';\n\nimport { badgeStyles } from '../../generated/styles';\nimport { HStack } from './HStack';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport { Text } from './Text';\n\ninterface BadgeProps extends ViewProps {\n /** The visual style variant of the badge. @default 'primary' */\n variant?: BadgeVariant;\n /** The size of the badge. @default 'md' */\n size?: BadgeSize;\n /** Minimum width of the badge in pixels. */\n minWidth?: number;\n /** Maximum width of the badge in pixels. @default 200 */\n maxWidth?: number;\n /** Icon to display at the start of the badge. */\n startIcon?: IconSlotType;\n /** Icon to display at the end of the badge. */\n endIcon?: IconSlotType;\n /** Override the background color. Use sparingly. */\n dangerouslySetBackgroundColor?: string;\n /** Override the text color. Use sparingly. */\n dangerouslySetColor?: string;\n /** Override the border color. Use sparingly. */\n dangerouslySetBorderColor?: string;\n /** Override the icon color. Use sparingly. */\n dangerouslySetIconColor?: string;\n /** Ref to the underlying View component. */\n ref?: Ref<View>;\n}\n\n/**\n * **A badge component for status indicators**\n *\n * @description\n * Badges show notifications, counts, or status information on navigation items and icons.\n *\n * @category Display\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Badge } from '@yahoo/uds-mobile';\n *\n * <Badge>Label</Badge>\n * <Badge variant=\"brand\" size=\"sm\">New</Badge>\n * <Badge variant=\"alert\" startIcon=\"Warning\">Error</Badge>\n * ```\n *\n * @accessibility\n * - Badge content is read by screen readers\n * - Use descriptive text for status indicators\n * - Consider `accessibilityLabel` for icon-only badges\n *\n * @see {@link Chip} for interactive tag-like elements\n */\nconst Badge = memo(function Badge({\n variant = 'primary',\n size = 'md',\n minWidth,\n maxWidth = 200,\n startIcon,\n endIcon,\n dangerouslySetBackgroundColor,\n dangerouslySetColor,\n dangerouslySetIconColor,\n dangerouslySetBorderColor,\n children,\n style,\n ref,\n ...rest\n}: BadgeProps) {\n badgeStyles.useVariants({ size, variant });\n const rootStyles = useMemo(() => [badgeStyles.root, style], [style, badgeStyles.root]);\n\n return (\n <HStack\n ref={ref}\n alignItems=\"center\"\n overflow=\"hidden\"\n alignSelf=\"flex-start\"\n maxWidth={maxWidth}\n minWidth={minWidth}\n dangerouslySetBackgroundColor={dangerouslySetBackgroundColor}\n dangerouslySetBorderColor={dangerouslySetBorderColor}\n // Cannot memoize - styles contain theme-reactive values\n style={rootStyles}\n {...rest}\n >\n {startIcon && (\n <IconSlot\n icon={startIcon}\n variant=\"fill\"\n dangerouslySetColor={dangerouslySetIconColor}\n style={badgeStyles.icon}\n />\n )}\n <Text\n numberOfLines={1}\n flexShrink=\"1\"\n dangerouslySetColor={dangerouslySetColor}\n style={badgeStyles.text}\n >\n {children}\n </Text>\n {endIcon && (\n <IconSlot\n icon={endIcon}\n variant=\"fill\"\n dangerouslySetColor={dangerouslySetIconColor}\n style={badgeStyles.icon}\n />\n )}\n </HStack>\n );\n});\n\nBadge.displayName = 'Badge';\n\nexport { Badge, type BadgeProps };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,MAAM,QAAQ,KAAK,SAAS,MAAM,EAChC,UAAU,WACV,OAAO,MACP,UACA,WAAW,KACX,WACA,SACA,+BACA,qBACA,yBACA,2BACA,UACA,OACA,KACA,GAAG,QACU;AACb,aAAY,YAAY;EAAE;EAAM;EAAS,CAAC;AAG1C,QACE,qBAAC,QAAD;EACO;EACL,YAAW;EACX,UAAS;EACT,WAAU;EACA;EACA;EACqB;EACJ;EAE3B,OAbe,cAAc,CAAC,YAAY,MAAM,MAAM,EAAE,CAAC,OAAO,YAAY,KAAK,CAahE;EACjB,GAAI;YAXN;GAaG,aACC,oBAAC,UAAD;IACE,MAAM;IACN,SAAQ;IACR,qBAAqB;IACrB,OAAO,YAAY;IACnB,CAAA;GAEJ,oBAAC,MAAD;IACE,eAAe;IACf,YAAW;IACU;IACrB,OAAO,YAAY;IAElB;IACI,CAAA;GACN,WACC,oBAAC,UAAD;IACE,MAAM;IACN,SAAQ;IACR,qBAAqB;IACrB,OAAO,YAAY;IACnB,CAAA;GAEG;;EAEX;AAEF,MAAM,cAAc"}
1
+ {"version":3,"file":"Badge.js","names":[],"sources":["../../src/components/Badge.tsx"],"sourcesContent":["import type { BadgeSize, BadgeVariant } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { memo, useMemo } from 'react';\nimport type { View, ViewProps } from 'react-native';\n\nimport { badgeStyles } from '../../generated/styles';\nimport { HStack } from './HStack';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport { Text } from './Text';\n\ninterface BadgeProps extends ViewProps {\n /** The visual style variant of the badge. @default 'primary' */\n variant?: BadgeVariant;\n /** The size of the badge. @default 'md' */\n size?: BadgeSize;\n /** Minimum width of the badge in pixels. */\n minWidth?: number;\n /** Maximum width of the badge in pixels. @default 200 */\n maxWidth?: number;\n /** Icon to display at the start of the badge. */\n startIcon?: IconSlotType;\n /** Icon to display at the end of the badge. */\n endIcon?: IconSlotType;\n /** Override the background color. Use sparingly. */\n dangerouslySetBackgroundColor?: string;\n /** Override the text color. Use sparingly. */\n dangerouslySetColor?: string;\n /** Override the border color. Use sparingly. */\n dangerouslySetBorderColor?: string;\n /** Override the icon color. Use sparingly. */\n dangerouslySetIconColor?: string;\n /** Ref to the underlying View component. */\n ref?: Ref<View>;\n}\n\n/**\n * **A badge component for status indicators**\n *\n * @description\n * Badges show notifications, counts, or status information on navigation items and icons.\n *\n * @category Display\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Badge } from '@yahoo/uds-mobile/Badge';\n *\n * <Badge>Label</Badge>\n * <Badge variant=\"brand\" size=\"sm\">New</Badge>\n * <Badge variant=\"alert\" startIcon=\"Warning\">Error</Badge>\n * ```\n *\n * @accessibility\n * - Badge content is read by screen readers\n * - Use descriptive text for status indicators\n * - Consider `accessibilityLabel` for icon-only badges\n *\n * @see {@link Chip} for interactive tag-like elements\n */\nconst Badge = memo(function Badge({\n variant = 'primary',\n size = 'md',\n minWidth,\n maxWidth = 200,\n startIcon,\n endIcon,\n dangerouslySetBackgroundColor,\n dangerouslySetColor,\n dangerouslySetIconColor,\n dangerouslySetBorderColor,\n children,\n style,\n ref,\n ...rest\n}: BadgeProps) {\n badgeStyles.useVariants({ size, variant });\n const rootStyles = useMemo(() => [badgeStyles.root, style], [style, badgeStyles.root]);\n\n return (\n <HStack\n ref={ref}\n alignItems=\"center\"\n overflow=\"hidden\"\n alignSelf=\"flex-start\"\n maxWidth={maxWidth}\n minWidth={minWidth}\n dangerouslySetBackgroundColor={dangerouslySetBackgroundColor}\n dangerouslySetBorderColor={dangerouslySetBorderColor}\n // Cannot memoize - styles contain theme-reactive values\n style={rootStyles}\n {...rest}\n >\n {startIcon && (\n <IconSlot\n icon={startIcon}\n variant=\"fill\"\n dangerouslySetColor={dangerouslySetIconColor}\n style={badgeStyles.icon}\n />\n )}\n <Text\n numberOfLines={1}\n flexShrink=\"1\"\n dangerouslySetColor={dangerouslySetColor}\n style={badgeStyles.text}\n >\n {children}\n </Text>\n {endIcon && (\n <IconSlot\n icon={endIcon}\n variant=\"fill\"\n dangerouslySetColor={dangerouslySetIconColor}\n style={badgeStyles.icon}\n />\n )}\n </HStack>\n );\n});\n\nBadge.displayName = 'Badge';\n\nexport { Badge, type BadgeProps };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,MAAM,QAAQ,KAAK,SAAS,MAAM,EAChC,UAAU,WACV,OAAO,MACP,UACA,WAAW,KACX,WACA,SACA,+BACA,qBACA,yBACA,2BACA,UACA,OACA,KACA,GAAG,QACU;CACb,YAAY,YAAY;EAAE;EAAM;EAAS,CAAC;CAG1C,OACE,qBAAC,QAAD;EACO;EACL,YAAW;EACX,UAAS;EACT,WAAU;EACA;EACA;EACqB;EACJ;EAE3B,OAbe,cAAc,CAAC,YAAY,MAAM,MAAM,EAAE,CAAC,OAAO,YAAY,KAAK,CAahE;EACjB,GAAI;YAXN;GAaG,aACC,oBAAC,UAAD;IACE,MAAM;IACN,SAAQ;IACR,qBAAqB;IACrB,OAAO,YAAY;IACnB,CAAA;GAEJ,oBAAC,MAAD;IACE,eAAe;IACf,YAAW;IACU;IACrB,OAAO,YAAY;IAElB;IACI,CAAA;GACN,WACC,oBAAC,UAAD;IACE,MAAM;IACN,SAAQ;IACR,qBAAqB;IACrB,OAAO,YAAY;IACnB,CAAA;GAEG;;EAEX;AAEF,MAAM,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"Banner.js","names":["RNView","Text"],"sources":["../../../src/components/Banner/Banner.tsx"],"sourcesContent":["import type { BannerVariant, UniversalBannerProps } from '@yahoo/uds-types';\nimport type { ReactNode, Ref } from 'react';\nimport { cloneElement, isValidElement, memo, useMemo } from 'react';\nimport type { View, ViewProps } from 'react-native';\nimport { StyleSheet, View as RNView } from 'react-native';\n\nimport { bannerStyles } from '../../../generated/styles';\nimport { IconButton } from '../IconButton';\nimport type { IconSlotType } from '../IconSlot';\nimport { IconSlot } from '../IconSlot';\nimport { Text } from '../Text';\nimport { BANNER_TO_BUTTON_VARIANT, hasDisplayName, separateChildren } from './utils';\n\n/**\n * Inject button variants on Button children that don't have an explicit\n * variant set. The last unset Button gets the banner-mapped \"primary\"\n * variant; any preceding unset Buttons get \"secondary\".\n * When stacked (3+ actions), buttons get full width.\n */\nfunction bindActionVariants(\n actions: ReactNode[],\n bannerVariant: BannerVariant,\n stacked: boolean,\n): ReactNode[] {\n const primaryVariant = BANNER_TO_BUTTON_VARIANT[bannerVariant];\n\n const unsetIndices: number[] = [];\n actions.forEach((child, i) => {\n if (\n hasDisplayName(child, 'Button') &&\n isValidElement<{ variant?: string }>(child) &&\n child.props.variant === undefined\n ) {\n unsetIndices.push(i);\n }\n });\n\n return actions.map((child, i) => {\n const extraProps: Record<string, unknown> = {};\n\n // Auto-bind variant on Buttons without explicit variant\n if (unsetIndices.includes(i)) {\n const isLast = i === unsetIndices[unsetIndices.length - 1];\n extraProps.variant = isLast ? primaryVariant : 'secondary';\n }\n\n // When stacked, non-Button children (e.g. Link) should not stretch full width\n if (stacked && !hasDisplayName(child, 'Button') && isValidElement<ViewProps>(child)) {\n extraProps.style = [child.props.style, { alignSelf: 'flex-start' as const }];\n }\n\n if (Object.keys(extraProps).length === 0) {\n return child;\n }\n\n return cloneElement(child as React.ReactElement, {\n key: (child as React.ReactElement).key ?? i,\n ...extraProps,\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Banner\n// ---------------------------------------------------------------------------\n\ninterface BannerProps extends ViewProps, UniversalBannerProps<IconSlotType> {\n /** Accessible label for the dismiss button. @default \"Dismiss\" */\n dismissAccessibilityLabel?: string;\n /** Ref to the underlying View. */\n ref?: Ref<View>;\n}\n\n/**\n * **An inline notification banner for contextual messages with optional actions.**\n *\n * @description\n * Banner is an inline, non-floating notification component that sits in normal document flow.\n * It supports 12 color variants, an optional icon, title, description (truncated to 3 lines),\n * composable actions via children, and an optional dismiss button.\n *\n * On mobile the layout is always column: content stacks above actions.\n *\n * @category Display\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Banner, BannerContent, BannerTitle, BannerDescription } from '@yahoo/uds-mobile/Banner';\n * import { Button } from '@yahoo/uds-mobile/Button';\n * import { Link } from '@yahoo/uds-mobile/Link';\n *\n * // Simple\n * <Banner variant=\"info\" startIcon=\"Info\" title=\"Update available\" />\n *\n * // With actions\n * <Banner variant=\"brand\" startIcon=\"Megaphone\" title=\"Notification\"\n * description=\"Description text.\" onDismiss={() => {}}>\n * <Link>Learn more</Link>\n * <Button size=\"sm\">Primary</Button>\n * </Banner>\n *\n * // Rich content\n * <Banner variant=\"alert\" startIcon=\"Error\" onDismiss={() => {}}>\n * <BannerContent>\n * <BannerTitle>Error occurred</BannerTitle>\n * <BannerDescription>Something went wrong.</BannerDescription>\n * </BannerContent>\n * <Button size=\"sm\">Retry</Button>\n * </Banner>\n * ```\n *\n * @see {@link Badge} for status indicators\n */\nconst Banner = memo(function Banner({\n variant = 'primary',\n startIcon,\n title,\n description,\n onDismiss,\n dismissAccessibilityLabel = 'Dismiss',\n children,\n style,\n ref,\n ...rest\n}: BannerProps) {\n const hasTitle = !!(typeof title === 'string' ? title.trim() : title);\n const hasDescription = !!(typeof description === 'string' ? description.trim() : description);\n\n bannerStyles.useVariants({ variant });\n\n const rootGap = bannerStyles.root.gap ?? 0;\n const closeIconSize = bannerStyles.close.iconSizeToken ?? 'xs';\n\n // Separate BannerContent from action children, bind button variants\n const { content: bannerContent, actions: rawActions } = separateChildren(children);\n const stacked = rawActions.length >= 3;\n const actions = bindActionVariants(rawActions, variant, stacked);\n const hasActions = actions.length > 0;\n\n // Build the content area: either explicit BannerContent or auto-generated from props\n const contentArea =\n bannerContent ??\n (hasTitle || hasDescription ? (\n <RNView style={internalStyles.contentArea}>\n {hasTitle && (\n <Text numberOfLines={1} style={bannerStyles.title}>\n {title}\n </Text>\n )}\n {hasDescription && (\n <Text numberOfLines={3} style={bannerStyles.description}>\n {description}\n </Text>\n )}\n </RNView>\n ) : null);\n\n // Center icon/dismiss when content is single-line (title-only or description-only).\n // BannerContent is excluded — we can't inspect its children to know the line count.\n const isSingleLine = !bannerContent && hasTitle !== hasDescription;\n\n const rootStyles = useMemo(\n () => [bannerStyles.root, internalStyles.root, style],\n [bannerStyles.root, style],\n );\n\n return (\n <RNView ref={ref} style={rootStyles} {...rest}>\n {startIcon && (\n <IconSlot\n icon={startIcon}\n variant=\"fill\"\n style={[\n bannerStyles.icon,\n internalStyles.iconShrink,\n isSingleLine ? internalStyles.alignCenter : internalStyles.alignStart,\n ]}\n />\n )}\n\n <RNView\n style={[\n internalStyles.innerWrapper,\n { rowGap: rootGap },\n isSingleLine ? internalStyles.alignCenter : undefined,\n ]}\n >\n {contentArea}\n\n {hasActions && (\n <RNView style={stacked ? internalStyles.actionsStacked : internalStyles.actionsRow}>\n {actions}\n </RNView>\n )}\n </RNView>\n\n {onDismiss && (\n <IconButton\n name=\"Cross\"\n variant=\"tertiary\"\n size={closeIconSize}\n iconColor=\"primary\"\n accessibilityLabel={dismissAccessibilityLabel}\n onPress={onDismiss}\n style={[\n internalStyles.dismissButton,\n isSingleLine ? internalStyles.alignCenter : internalStyles.alignStart,\n ]}\n hitSlop={12}\n disableEffects\n />\n )}\n </RNView>\n );\n});\n\nBanner.displayName = 'Banner';\n\nconst internalStyles = StyleSheet.create({\n root: {\n flexDirection: 'row',\n minWidth: 300,\n },\n alignCenter: {\n alignSelf: 'center',\n },\n innerWrapper: {\n flex: 1,\n minWidth: 0,\n flexDirection: 'column',\n alignItems: 'flex-start',\n },\n contentArea: {\n minWidth: 0,\n rowGap: 2,\n },\n actionsRow: {\n flexDirection: 'row',\n alignItems: 'center',\n alignSelf: 'stretch',\n columnGap: 6,\n },\n actionsStacked: {\n flexDirection: 'column',\n alignItems: 'stretch',\n alignSelf: 'stretch',\n rowGap: 6,\n },\n iconShrink: {\n flexShrink: 0,\n },\n dismissButton: {\n flexShrink: 0,\n },\n alignStart: {\n alignSelf: 'flex-start',\n },\n});\n\nexport { Banner, type BannerProps };\n"],"mappings":"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"}
1
+ {"version":3,"file":"Banner.js","names":["RNView","Text"],"sources":["../../../src/components/Banner/Banner.tsx"],"sourcesContent":["import type { BannerVariant, UniversalBannerProps } from '@yahoo/uds-types';\nimport type { ReactNode, Ref } from 'react';\nimport { cloneElement, isValidElement, memo, useMemo } from 'react';\nimport type { View, ViewProps } from 'react-native';\nimport { StyleSheet, View as RNView } from 'react-native';\n\nimport { bannerStyles } from '../../../generated/styles';\nimport { IconButton } from '../IconButton';\nimport type { IconSlotType } from '../IconSlot';\nimport { IconSlot } from '../IconSlot';\nimport { Text } from '../Text';\nimport { BANNER_TO_BUTTON_VARIANT, hasDisplayName, separateChildren } from './utils';\n\n/**\n * Inject button variants on Button children that don't have an explicit\n * variant set. The last unset Button gets the banner-mapped \"primary\"\n * variant; any preceding unset Buttons get \"secondary\".\n * When stacked (3+ actions), buttons get full width.\n */\nfunction bindActionVariants(\n actions: ReactNode[],\n bannerVariant: BannerVariant,\n stacked: boolean,\n): ReactNode[] {\n const primaryVariant = BANNER_TO_BUTTON_VARIANT[bannerVariant];\n\n const unsetIndices: number[] = [];\n actions.forEach((child, i) => {\n if (\n hasDisplayName(child, 'Button') &&\n isValidElement<{ variant?: string }>(child) &&\n child.props.variant === undefined\n ) {\n unsetIndices.push(i);\n }\n });\n\n return actions.map((child, i) => {\n const extraProps: Record<string, unknown> = {};\n\n // Auto-bind variant on Buttons without explicit variant\n if (unsetIndices.includes(i)) {\n const isLast = i === unsetIndices[unsetIndices.length - 1];\n extraProps.variant = isLast ? primaryVariant : 'secondary';\n }\n\n // When stacked, non-Button children (e.g. Link) should not stretch full width\n if (stacked && !hasDisplayName(child, 'Button') && isValidElement<ViewProps>(child)) {\n extraProps.style = [child.props.style, { alignSelf: 'flex-start' as const }];\n }\n\n if (Object.keys(extraProps).length === 0) {\n return child;\n }\n\n return cloneElement(child as React.ReactElement, {\n key: (child as React.ReactElement).key ?? i,\n ...extraProps,\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Banner\n// ---------------------------------------------------------------------------\n\ninterface BannerProps extends ViewProps, UniversalBannerProps<IconSlotType> {\n /** Accessible label for the dismiss button. @default \"Dismiss\" */\n dismissAccessibilityLabel?: string;\n /** Ref to the underlying View. */\n ref?: Ref<View>;\n}\n\n/**\n * **An inline notification banner for contextual messages with optional actions.**\n *\n * @description\n * Banner is an inline, non-floating notification component that sits in normal document flow.\n * It supports 12 color variants, an optional icon, title, description (truncated to 3 lines),\n * composable actions via children, and an optional dismiss button.\n *\n * On mobile the layout is always column: content stacks above actions.\n *\n * @category Display\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Banner, BannerContent, BannerTitle, BannerDescription } from '@yahoo/uds-mobile/Banner';\n * import { Button } from '@yahoo/uds-mobile/Button';\n * import { Link } from '@yahoo/uds-mobile/Link';\n *\n * // Simple\n * <Banner variant=\"info\" startIcon=\"Info\" title=\"Update available\" />\n *\n * // With actions\n * <Banner variant=\"brand\" startIcon=\"Megaphone\" title=\"Notification\"\n * description=\"Description text.\" onDismiss={() => {}}>\n * <Link>Learn more</Link>\n * <Button size=\"sm\">Primary</Button>\n * </Banner>\n *\n * // Rich content\n * <Banner variant=\"alert\" startIcon=\"Error\" onDismiss={() => {}}>\n * <BannerContent>\n * <BannerTitle>Error occurred</BannerTitle>\n * <BannerDescription>Something went wrong.</BannerDescription>\n * </BannerContent>\n * <Button size=\"sm\">Retry</Button>\n * </Banner>\n * ```\n *\n * @see {@link Badge} for status indicators\n */\nconst Banner = memo(function Banner({\n variant = 'primary',\n startIcon,\n title,\n description,\n onDismiss,\n dismissAccessibilityLabel = 'Dismiss',\n children,\n style,\n ref,\n ...rest\n}: BannerProps) {\n const hasTitle = !!(typeof title === 'string' ? title.trim() : title);\n const hasDescription = !!(typeof description === 'string' ? description.trim() : description);\n\n bannerStyles.useVariants({ variant });\n\n const rootGap = bannerStyles.root.gap ?? 0;\n const closeIconSize = bannerStyles.close.iconSizeToken ?? 'xs';\n\n // Separate BannerContent from action children, bind button variants\n const { content: bannerContent, actions: rawActions } = separateChildren(children);\n const stacked = rawActions.length >= 3;\n const actions = bindActionVariants(rawActions, variant, stacked);\n const hasActions = actions.length > 0;\n\n // Build the content area: either explicit BannerContent or auto-generated from props\n const contentArea =\n bannerContent ??\n (hasTitle || hasDescription ? (\n <RNView style={internalStyles.contentArea}>\n {hasTitle && (\n <Text numberOfLines={1} style={bannerStyles.title}>\n {title}\n </Text>\n )}\n {hasDescription && (\n <Text numberOfLines={3} style={bannerStyles.description}>\n {description}\n </Text>\n )}\n </RNView>\n ) : null);\n\n // Center icon/dismiss when content is single-line (title-only or description-only).\n // BannerContent is excluded — we can't inspect its children to know the line count.\n const isSingleLine = !bannerContent && hasTitle !== hasDescription;\n\n const rootStyles = useMemo(\n () => [bannerStyles.root, internalStyles.root, style],\n [bannerStyles.root, style],\n );\n\n return (\n <RNView ref={ref} style={rootStyles} {...rest}>\n {startIcon && (\n <IconSlot\n icon={startIcon}\n variant=\"fill\"\n style={[\n bannerStyles.icon,\n internalStyles.iconShrink,\n isSingleLine ? internalStyles.alignCenter : internalStyles.alignStart,\n ]}\n />\n )}\n\n <RNView\n style={[\n internalStyles.innerWrapper,\n { rowGap: rootGap },\n isSingleLine ? internalStyles.alignCenter : undefined,\n ]}\n >\n {contentArea}\n\n {hasActions && (\n <RNView style={stacked ? internalStyles.actionsStacked : internalStyles.actionsRow}>\n {actions}\n </RNView>\n )}\n </RNView>\n\n {onDismiss && (\n <IconButton\n name=\"Cross\"\n variant=\"tertiary\"\n size={closeIconSize}\n iconColor=\"primary\"\n accessibilityLabel={dismissAccessibilityLabel}\n onPress={onDismiss}\n style={[\n internalStyles.dismissButton,\n isSingleLine ? internalStyles.alignCenter : internalStyles.alignStart,\n ]}\n hitSlop={12}\n disableEffects\n />\n )}\n </RNView>\n );\n});\n\nBanner.displayName = 'Banner';\n\nconst internalStyles = StyleSheet.create({\n root: {\n flexDirection: 'row',\n minWidth: 300,\n },\n alignCenter: {\n alignSelf: 'center',\n },\n innerWrapper: {\n flex: 1,\n minWidth: 0,\n flexDirection: 'column',\n alignItems: 'flex-start',\n },\n contentArea: {\n minWidth: 0,\n rowGap: 2,\n },\n actionsRow: {\n flexDirection: 'row',\n alignItems: 'center',\n alignSelf: 'stretch',\n columnGap: 6,\n },\n actionsStacked: {\n flexDirection: 'column',\n alignItems: 'stretch',\n alignSelf: 'stretch',\n rowGap: 6,\n },\n iconShrink: {\n flexShrink: 0,\n },\n dismissButton: {\n flexShrink: 0,\n },\n alignStart: {\n alignSelf: 'flex-start',\n },\n});\n\nexport { Banner, type BannerProps };\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"BannerContent.js","names":[],"sources":["../../../src/components/Banner/BannerContent.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { memo } from 'react';\nimport { View } from 'react-native';\n\ninterface BannerContentProps {\n children: ReactNode;\n}\n\n/**\n * Container for rich banner content using BannerTitle and BannerDescription.\n * When used as a child of Banner, replaces the auto-generated title/description area.\n */\nconst BannerContent = memo(function BannerContent({ children }: BannerContentProps) {\n return <View style={{ flex: 1, minWidth: 0, rowGap: 2 }}>{children}</View>;\n});\n\nBannerContent.displayName = 'BannerContent';\n\nexport { BannerContent, type BannerContentProps };\n"],"mappings":";;;;;;;;;AAYA,MAAM,gBAAgB,KAAK,SAAS,cAAc,EAAE,YAAgC;AAClF,QAAO,oBAAC,MAAD;EAAM,OAAO;GAAE,MAAM;GAAG,UAAU;GAAG,QAAQ;GAAG;EAAG;EAAgB,CAAA;EAC1E;AAEF,cAAc,cAAc"}
1
+ {"version":3,"file":"BannerContent.js","names":[],"sources":["../../../src/components/Banner/BannerContent.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { memo } from 'react';\nimport { View } from 'react-native';\n\ninterface BannerContentProps {\n children: ReactNode;\n}\n\n/**\n * Container for rich banner content using BannerTitle and BannerDescription.\n * When used as a child of Banner, replaces the auto-generated title/description area.\n */\nconst BannerContent = memo(function BannerContent({ children }: BannerContentProps) {\n return <View style={{ flex: 1, minWidth: 0, rowGap: 2 }}>{children}</View>;\n});\n\nBannerContent.displayName = 'BannerContent';\n\nexport { BannerContent, type BannerContentProps };\n"],"mappings":";;;;;;;;;AAYA,MAAM,gBAAgB,KAAK,SAAS,cAAc,EAAE,YAAgC;CAClF,OAAO,oBAAC,MAAD;EAAM,OAAO;GAAE,MAAM;GAAG,UAAU;GAAG,QAAQ;GAAG;EAAG;EAAgB,CAAA;EAC1E;AAEF,cAAc,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"BannerDescription.js","names":[],"sources":["../../../src/components/Banner/BannerDescription.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { memo } from 'react';\n\nimport { bannerStyles } from '../../../generated/styles';\nimport { Text } from '../Text';\n\ninterface BannerDescriptionProps {\n children: ReactNode;\n}\n\n/**\n * **Styled description for use inside `BannerContent`.**\n *\n * @description\n * Applies the Banner description typography tokens and truncates to 3 lines\n * via `numberOfLines={3}`. Use this subcomponent instead of the `description`\n * prop when you need rich ReactNode content.\n *\n * @platform mobile\n *\n * @example\n * ```tsx\n * <Banner variant=\"alert\" startIcon=\"Error\">\n * <BannerContent>\n * <BannerTitle>Something went wrong</BannerTitle>\n * <BannerDescription>\n * Please try again or contact support for help.\n * </BannerDescription>\n * </BannerContent>\n * </Banner>\n * ```\n *\n * @see {@link Banner} for the parent component\n * @see {@link BannerContent} for the required wrapper\n */\nconst BannerDescription = memo(function BannerDescription({ children }: BannerDescriptionProps) {\n bannerStyles.useVariants({});\n\n return (\n <Text numberOfLines={3} style={bannerStyles.description}>\n {children}\n </Text>\n );\n});\n\nBannerDescription.displayName = 'BannerDescription';\n\nexport { BannerDescription, type BannerDescriptionProps };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAM,oBAAoB,KAAK,SAAS,kBAAkB,EAAE,YAAoC;AAC9F,cAAa,YAAY,EAAE,CAAC;AAE5B,QACE,oBAAC,MAAD;EAAM,eAAe;EAAG,OAAO,aAAa;EACzC;EACI,CAAA;EAET;AAEF,kBAAkB,cAAc"}
1
+ {"version":3,"file":"BannerDescription.js","names":[],"sources":["../../../src/components/Banner/BannerDescription.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { memo } from 'react';\n\nimport { bannerStyles } from '../../../generated/styles';\nimport { Text } from '../Text';\n\ninterface BannerDescriptionProps {\n children: ReactNode;\n}\n\n/**\n * **Styled description for use inside `BannerContent`.**\n *\n * @description\n * Applies the Banner description typography tokens and truncates to 3 lines\n * via `numberOfLines={3}`. Use this subcomponent instead of the `description`\n * prop when you need rich ReactNode content.\n *\n * @platform mobile\n *\n * @example\n * ```tsx\n * <Banner variant=\"alert\" startIcon=\"Error\">\n * <BannerContent>\n * <BannerTitle>Something went wrong</BannerTitle>\n * <BannerDescription>\n * Please try again or contact support for help.\n * </BannerDescription>\n * </BannerContent>\n * </Banner>\n * ```\n *\n * @see {@link Banner} for the parent component\n * @see {@link BannerContent} for the required wrapper\n */\nconst BannerDescription = memo(function BannerDescription({ children }: BannerDescriptionProps) {\n bannerStyles.useVariants({});\n\n return (\n <Text numberOfLines={3} style={bannerStyles.description}>\n {children}\n </Text>\n );\n});\n\nBannerDescription.displayName = 'BannerDescription';\n\nexport { BannerDescription, type BannerDescriptionProps };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAM,oBAAoB,KAAK,SAAS,kBAAkB,EAAE,YAAoC;CAC9F,aAAa,YAAY,EAAE,CAAC;CAE5B,OACE,oBAAC,MAAD;EAAM,eAAe;EAAG,OAAO,aAAa;EACzC;EACI,CAAA;EAET;AAEF,kBAAkB,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"BannerTitle.js","names":[],"sources":["../../../src/components/Banner/BannerTitle.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { memo } from 'react';\n\nimport { bannerStyles } from '../../../generated/styles';\nimport { Text } from '../Text';\n\ninterface BannerTitleProps {\n children: ReactNode;\n}\n\n/**\n * **Styled title for use inside `BannerContent`.**\n *\n * @description\n * Applies the Banner title typography tokens. Renders a single line of text.\n * Use this subcomponent instead of the `title` prop when you need rich\n * ReactNode content (e.g. bold segments, inline icons).\n *\n * @platform mobile\n *\n * @example\n * ```tsx\n * <Banner variant=\"info\" startIcon=\"Info\">\n * <BannerContent>\n * <BannerTitle>Update available</BannerTitle>\n * <BannerDescription>A new version is ready to install.</BannerDescription>\n * </BannerContent>\n * </Banner>\n * ```\n *\n * @see {@link Banner} for the parent component\n * @see {@link BannerContent} for the required wrapper\n */\nconst BannerTitle = memo(function BannerTitle({ children }: BannerTitleProps) {\n bannerStyles.useVariants({});\n\n return (\n <Text numberOfLines={1} style={bannerStyles.title}>\n {children}\n </Text>\n );\n});\n\nBannerTitle.displayName = 'BannerTitle';\n\nexport { BannerTitle, type BannerTitleProps };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAM,cAAc,KAAK,SAAS,YAAY,EAAE,YAA8B;AAC5E,cAAa,YAAY,EAAE,CAAC;AAE5B,QACE,oBAAC,MAAD;EAAM,eAAe;EAAG,OAAO,aAAa;EACzC;EACI,CAAA;EAET;AAEF,YAAY,cAAc"}
1
+ {"version":3,"file":"BannerTitle.js","names":[],"sources":["../../../src/components/Banner/BannerTitle.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { memo } from 'react';\n\nimport { bannerStyles } from '../../../generated/styles';\nimport { Text } from '../Text';\n\ninterface BannerTitleProps {\n children: ReactNode;\n}\n\n/**\n * **Styled title for use inside `BannerContent`.**\n *\n * @description\n * Applies the Banner title typography tokens. Renders a single line of text.\n * Use this subcomponent instead of the `title` prop when you need rich\n * ReactNode content (e.g. bold segments, inline icons).\n *\n * @platform mobile\n *\n * @example\n * ```tsx\n * <Banner variant=\"info\" startIcon=\"Info\">\n * <BannerContent>\n * <BannerTitle>Update available</BannerTitle>\n * <BannerDescription>A new version is ready to install.</BannerDescription>\n * </BannerContent>\n * </Banner>\n * ```\n *\n * @see {@link Banner} for the parent component\n * @see {@link BannerContent} for the required wrapper\n */\nconst BannerTitle = memo(function BannerTitle({ children }: BannerTitleProps) {\n bannerStyles.useVariants({});\n\n return (\n <Text numberOfLines={1} style={bannerStyles.title}>\n {children}\n </Text>\n );\n});\n\nBannerTitle.displayName = 'BannerTitle';\n\nexport { BannerTitle, type BannerTitleProps };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAM,cAAc,KAAK,SAAS,YAAY,EAAE,YAA8B;CAC5E,aAAa,YAAY,EAAE,CAAC;CAE5B,OACE,oBAAC,MAAD;EAAM,eAAe;EAAG,OAAO,aAAa;EACzC;EACI,CAAA;EAET;AAEF,YAAY,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../../src/components/Banner/utils.ts"],"sourcesContent":["import type { BannerVariant, ButtonVariantFlat } from '@yahoo/uds-types';\nimport type { ReactNode } from 'react';\nimport { Children, isValidElement } from 'react';\n\n/**\n * Maps banner variants to button variants following the Figma spec.\n * Secondary banner variants map to secondary button variants.\n */\nexport const BANNER_TO_BUTTON_VARIANT: Record<BannerVariant, ButtonVariantFlat> = {\n primary: 'brand',\n secondary: 'brand',\n brand: 'brand',\n 'brand-secondary': 'brand-secondary',\n alert: 'alert',\n 'alert-secondary': 'alert-secondary',\n warning: 'warning',\n 'warning-secondary': 'warning-secondary',\n success: 'positive',\n 'success-secondary': 'positive-secondary',\n info: 'info',\n 'info-secondary': 'info-secondary',\n};\n\n/**\n * Checks whether a React element has a specific displayName.\n * Handles both regular function components and forwardRef components\n * (which have typeof type === 'object').\n */\nexport function hasDisplayName(child: ReactNode, name: string): boolean {\n if (!isValidElement(child)) {\n return false;\n }\n const type = child.type;\n return (\n (typeof type === 'function' || (typeof type === 'object' && type !== null)) &&\n 'displayName' in type &&\n type.displayName === name\n );\n}\n\n/**\n * Separates Banner children into content (BannerContent) and action elements.\n * Skips falsy children so conditional patterns like {show && <Button />} work.\n */\nexport function separateChildren(children: ReactNode): {\n content: ReactNode | null;\n actions: ReactNode[];\n} {\n let content: ReactNode | null = null;\n const actions: ReactNode[] = [];\n\n Children.forEach(children, (child) => {\n if (child == null || child === false || child === true || child === '') {\n return;\n }\n if (hasDisplayName(child, 'BannerContent')) {\n content = child;\n } else {\n actions.push(child);\n }\n });\n\n return { content, actions };\n}\n"],"mappings":";;;;;;;AAQA,MAAa,2BAAqE;CAChF,SAAS;CACT,WAAW;CACX,OAAO;CACP,mBAAmB;CACnB,OAAO;CACP,mBAAmB;CACnB,SAAS;CACT,qBAAqB;CACrB,SAAS;CACT,qBAAqB;CACrB,MAAM;CACN,kBAAkB;CACnB;;;;;;AAOD,SAAgB,eAAe,OAAkB,MAAuB;AACtE,KAAI,CAAC,eAAe,MAAM,CACxB,QAAO;CAET,MAAM,OAAO,MAAM;AACnB,SACG,OAAO,SAAS,cAAe,OAAO,SAAS,YAAY,SAAS,SACrE,iBAAiB,QACjB,KAAK,gBAAgB;;;;;;AAQzB,SAAgB,iBAAiB,UAG/B;CACA,IAAI,UAA4B;CAChC,MAAM,UAAuB,EAAE;AAE/B,UAAS,QAAQ,WAAW,UAAU;AACpC,MAAI,SAAS,QAAQ,UAAU,SAAS,UAAU,QAAQ,UAAU,GAClE;AAEF,MAAI,eAAe,OAAO,gBAAgB,CACxC,WAAU;MAEV,SAAQ,KAAK,MAAM;GAErB;AAEF,QAAO;EAAE;EAAS;EAAS"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../../src/components/Banner/utils.ts"],"sourcesContent":["import type { BannerVariant, ButtonVariantFlat } from '@yahoo/uds-types';\nimport type { ReactNode } from 'react';\nimport { Children, isValidElement } from 'react';\n\n/**\n * Maps banner variants to button variants following the Figma spec.\n * Secondary banner variants map to secondary button variants.\n */\nexport const BANNER_TO_BUTTON_VARIANT: Record<BannerVariant, ButtonVariantFlat> = {\n primary: 'brand',\n secondary: 'brand',\n brand: 'brand',\n 'brand-secondary': 'brand-secondary',\n alert: 'alert',\n 'alert-secondary': 'alert-secondary',\n warning: 'warning',\n 'warning-secondary': 'warning-secondary',\n success: 'positive',\n 'success-secondary': 'positive-secondary',\n info: 'info',\n 'info-secondary': 'info-secondary',\n};\n\n/**\n * Checks whether a React element has a specific displayName.\n * Handles both regular function components and forwardRef components\n * (which have typeof type === 'object').\n */\nexport function hasDisplayName(child: ReactNode, name: string): boolean {\n if (!isValidElement(child)) {\n return false;\n }\n const type = child.type;\n return (\n (typeof type === 'function' || (typeof type === 'object' && type !== null)) &&\n 'displayName' in type &&\n type.displayName === name\n );\n}\n\n/**\n * Separates Banner children into content (BannerContent) and action elements.\n * Skips falsy children so conditional patterns like {show && <Button />} work.\n */\nexport function separateChildren(children: ReactNode): {\n content: ReactNode | null;\n actions: ReactNode[];\n} {\n let content: ReactNode | null = null;\n const actions: ReactNode[] = [];\n\n Children.forEach(children, (child) => {\n if (child == null || child === false || child === true || child === '') {\n return;\n }\n if (hasDisplayName(child, 'BannerContent')) {\n content = child;\n } else {\n actions.push(child);\n }\n });\n\n return { content, actions };\n}\n"],"mappings":";;;;;;;AAQA,MAAa,2BAAqE;CAChF,SAAS;CACT,WAAW;CACX,OAAO;CACP,mBAAmB;CACnB,OAAO;CACP,mBAAmB;CACnB,SAAS;CACT,qBAAqB;CACrB,SAAS;CACT,qBAAqB;CACrB,MAAM;CACN,kBAAkB;CACnB;;;;;;AAOD,SAAgB,eAAe,OAAkB,MAAuB;CACtE,IAAI,CAAC,eAAe,MAAM,EACxB,OAAO;CAET,MAAM,OAAO,MAAM;CACnB,QACG,OAAO,SAAS,cAAe,OAAO,SAAS,YAAY,SAAS,SACrE,iBAAiB,QACjB,KAAK,gBAAgB;;;;;;AAQzB,SAAgB,iBAAiB,UAG/B;CACA,IAAI,UAA4B;CAChC,MAAM,UAAuB,EAAE;CAE/B,SAAS,QAAQ,WAAW,UAAU;EACpC,IAAI,SAAS,QAAQ,UAAU,SAAS,UAAU,QAAQ,UAAU,IAClE;EAEF,IAAI,eAAe,OAAO,gBAAgB,EACxC,UAAU;OAEV,QAAQ,KAAK,MAAM;GAErB;CAEF,OAAO;EAAE;EAAS;EAAS"}
@@ -44,7 +44,8 @@ function useBlurTargetView() {
44
44
  * @example
45
45
  * ```tsx
46
46
  * import { BlurTarget } from '@yahoo/uds-mobile/BlurTarget';
47
- * import { Box, Text } from '@yahoo/uds-mobile';
47
+ * import { Box } from '@yahoo/uds-mobile/Box';
48
+ import { Text } from '@yahoo/uds-mobile/Text';
48
49
  * import { useRef } from 'react';
49
50
  * import type { View } from 'react-native';
50
51
  *
@@ -23,7 +23,8 @@ interface BlurTargetProps {
23
23
  * @example
24
24
  * ```tsx
25
25
  * import { BlurTarget } from '@yahoo/uds-mobile/BlurTarget';
26
- * import { Box, Text } from '@yahoo/uds-mobile';
26
+ * import { Box } from '@yahoo/uds-mobile/Box';
27
+ import { Text } from '@yahoo/uds-mobile/Text';
27
28
  * import { useRef } from 'react';
28
29
  * import type { View } from 'react-native';
29
30
  *
@@ -1 +1 @@
1
- {"version":3,"file":"BlurTarget.d.cts","names":[],"sources":["../../src/components/BlurTarget.tsx"],"mappings":";;;;;;UAkDU,eAAA;EACR,QAAA,EAAU,SAAA;EADF;EAGR,IAAA;EACA,KAAA,GAAQ,SAAA;AAAA;;;;;;;;AAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAuCb,UAAA,EAAU,OAAA,CAAA,yBAAA,CAAA,eAAA,GAAA,OAAA,CAAA,aAAA,CAAA,IAAA"}
1
+ {"version":3,"file":"BlurTarget.d.cts","names":[],"sources":["../../src/components/BlurTarget.tsx"],"mappings":";;;;;;UAkDU,eAAA;EACR,QAAA,EAAU,SAAA;EADF;EAGR,IAAA;EACA,KAAA,GAAQ,SAAA;AAAA;;;;;;;;AAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAwCb,UAAA,EAAU,OAAA,CAAA,yBAAA,CAAA,eAAA,GAAA,OAAA,CAAA,aAAA,CAAA,IAAA"}
@@ -23,7 +23,8 @@ interface BlurTargetProps {
23
23
  * @example
24
24
  * ```tsx
25
25
  * import { BlurTarget } from '@yahoo/uds-mobile/BlurTarget';
26
- * import { Box, Text } from '@yahoo/uds-mobile';
26
+ * import { Box } from '@yahoo/uds-mobile/Box';
27
+ import { Text } from '@yahoo/uds-mobile/Text';
27
28
  * import { useRef } from 'react';
28
29
  * import type { View } from 'react-native';
29
30
  *
@@ -1 +1 @@
1
- {"version":3,"file":"BlurTarget.d.ts","names":[],"sources":["../../src/components/BlurTarget.tsx"],"mappings":";;;;;;UAkDU,eAAA;EACR,QAAA,EAAU,SAAA;EADF;EAGR,IAAA;EACA,KAAA,GAAQ,SAAA;AAAA;;;;;;;;AAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAuCb,UAAA,EAAU,OAAA,CAAA,yBAAA,CAAA,eAAA,GAAA,OAAA,CAAA,aAAA,CAAA,IAAA"}
1
+ {"version":3,"file":"BlurTarget.d.ts","names":[],"sources":["../../src/components/BlurTarget.tsx"],"mappings":";;;;;;UAkDU,eAAA;EACR,QAAA,EAAU,SAAA;EADF;EAGR,IAAA;EACA,KAAA,GAAQ,SAAA;AAAA;;;;;;;;AAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAwCb,UAAA,EAAU,OAAA,CAAA,yBAAA,CAAA,eAAA,GAAA,OAAA,CAAA,aAAA,CAAA,IAAA"}
@@ -42,7 +42,8 @@ function useBlurTargetView() {
42
42
  * @example
43
43
  * ```tsx
44
44
  * import { BlurTarget } from '@yahoo/uds-mobile/BlurTarget';
45
- * import { Box, Text } from '@yahoo/uds-mobile';
45
+ * import { Box } from '@yahoo/uds-mobile/Box';
46
+ import { Text } from '@yahoo/uds-mobile/Text';
46
47
  * import { useRef } from 'react';
47
48
  * import type { View } from 'react-native';
48
49
  *
@@ -1 +1 @@
1
- {"version":3,"file":"BlurTarget.js","names":[],"sources":["../../src/components/BlurTarget.tsx"],"sourcesContent":["import type { ComponentType, ReactNode, RefObject } from 'react';\nimport { forwardRef, useEffect, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Platform, StyleSheet, View } from 'react-native';\n\n/** Props for expo-blur's BlurTargetView component */\ninterface BlurTargetViewProps {\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n ref?: RefObject<View | null>;\n}\n\n// Optional expo-blur dependency - loaded via dynamic import for Metro compatibility\n// Metro can statically analyze import() and will include expo-blur in the bundle if installed\nlet BlurTargetView: ComponentType<BlurTargetViewProps> | null = null;\nlet blurTargetLoadState: 'pending' | 'loaded' | 'failed' = 'pending';\nconst blurTargetLoadListeners: (() => void)[] = [];\n\n// Start loading expo-blur immediately (Metro will bundle it if installed)\nimport('expo-blur')\n .then((mod) => {\n BlurTargetView = mod.BlurTargetView;\n blurTargetLoadState = 'loaded';\n blurTargetLoadListeners.forEach((cb) => cb());\n })\n .catch(() => {\n blurTargetLoadState = 'failed';\n blurTargetLoadListeners.forEach((cb) => cb());\n });\n\n/** Hook to get BlurTargetView component, re-renders when loaded */\nfunction useBlurTargetView() {\n const [, forceUpdate] = useState(0);\n\n useEffect(() => {\n if (blurTargetLoadState === 'pending') {\n const listener = () => forceUpdate((n) => n + 1);\n blurTargetLoadListeners.push(listener);\n return () => {\n const idx = blurTargetLoadListeners.indexOf(listener);\n if (idx >= 0) {\n blurTargetLoadListeners.splice(idx, 1);\n }\n };\n }\n }, []);\n\n return BlurTargetView;\n}\n\ninterface BlurTargetProps {\n children: ReactNode;\n /** Fill the parent container (applies absoluteFill positioning) */\n fill?: boolean;\n style?: ViewStyle;\n}\n\n/**\n * **🔲 Wrapper for content that can be blurred**\n *\n * @description\n * When using blur effects (via the `blur` prop or elevation with blur configured),\n * wrap the content you want to blur in this component and pass its ref to Box's `blurTarget` prop.\n *\n * BlurTarget handles platform differences internally - on iOS it renders children directly,\n * on Android it wraps them in the native view required for blur to work.\n *\n * @example\n * ```tsx\n * import { BlurTarget } from '@yahoo/uds-mobile/BlurTarget';\n * import { Box, Text } from '@yahoo/uds-mobile';\n * import { useRef } from 'react';\n * import type { View } from 'react-native';\n *\n * function MyComponent() {\n * const blurTargetRef = useRef<View>(null);\n *\n * return (\n * <Box flex=\"1\">\n * <BlurTarget ref={blurTargetRef} fill>\n * <Image source={backgroundImage} />\n * </BlurTarget>\n *\n * <Box blur={50} blurTarget={blurTargetRef} borderRadius=\"lg\" spacing=\"4\">\n * <Text>Overlay content</Text>\n * </Box>\n * </Box>\n * );\n * }\n * ```\n *\n * @see {@link https://docs.expo.dev/versions/latest/sdk/blur-view/} expo-blur documentation\n */\nconst BlurTarget = forwardRef<View | null, BlurTargetProps>(function BlurTarget(\n { children, fill, style },\n ref,\n) {\n const BlurTargetViewComponent = useBlurTargetView();\n const resolvedStyle = fill ? [StyleSheet.absoluteFill, style] : style;\n\n // On Android with expo-blur, wrap in BlurTargetView for blur to work\n if (Platform.OS === 'android' && BlurTargetViewComponent) {\n return (\n <BlurTargetViewComponent ref={ref as RefObject<View | null>} style={resolvedStyle}>\n {children}\n </BlurTargetViewComponent>\n );\n }\n\n // On iOS (or if expo-blur not installed), wrap in View if style/ref is provided\n // No special BlurTargetView needed - blur works automatically on iOS\n if (resolvedStyle || ref) {\n return (\n <View ref={ref} style={resolvedStyle}>\n {children}\n </View>\n );\n }\n\n // No wrapper needed if no style/ref\n return children;\n});\n\nBlurTarget.displayName = 'BlurTarget';\n\nexport { BlurTarget, type BlurTargetProps };\n"],"mappings":";;;;;AAcA,IAAI,iBAA4D;AAChE,IAAI,sBAAuD;AAC3D,MAAM,0BAA0C,EAAE;AAGlD,OAAO,aACJ,MAAM,QAAQ;AACb,kBAAiB,IAAI;AACrB,uBAAsB;AACtB,yBAAwB,SAAS,OAAO,IAAI,CAAC;EAC7C,CACD,YAAY;AACX,uBAAsB;AACtB,yBAAwB,SAAS,OAAO,IAAI,CAAC;EAC7C;;AAGJ,SAAS,oBAAoB;CAC3B,MAAM,GAAG,eAAe,SAAS,EAAE;AAEnC,iBAAgB;AACd,MAAI,wBAAwB,WAAW;GACrC,MAAM,iBAAiB,aAAa,MAAM,IAAI,EAAE;AAChD,2BAAwB,KAAK,SAAS;AACtC,gBAAa;IACX,MAAM,MAAM,wBAAwB,QAAQ,SAAS;AACrD,QAAI,OAAO,EACT,yBAAwB,OAAO,KAAK,EAAE;;;IAI3C,EAAE,CAAC;AAEN,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CT,MAAM,aAAa,WAAyC,SAAS,WACnE,EAAE,UAAU,MAAM,SAClB,KACA;CACA,MAAM,0BAA0B,mBAAmB;CACnD,MAAM,gBAAgB,OAAO,CAAC,WAAW,cAAc,MAAM,GAAG;AAGhE,KAAI,SAAS,OAAO,aAAa,wBAC/B,QACE,oBAAC,yBAAD;EAA8B;EAA+B,OAAO;EACjE;EACuB,CAAA;AAM9B,KAAI,iBAAiB,IACnB,QACE,oBAAC,MAAD;EAAW;EAAK,OAAO;EACpB;EACI,CAAA;AAKX,QAAO;EACP;AAEF,WAAW,cAAc"}
1
+ {"version":3,"file":"BlurTarget.js","names":[],"sources":["../../src/components/BlurTarget.tsx"],"sourcesContent":["import type { ComponentType, ReactNode, RefObject } from 'react';\nimport { forwardRef, useEffect, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Platform, StyleSheet, View } from 'react-native';\n\n/** Props for expo-blur's BlurTargetView component */\ninterface BlurTargetViewProps {\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n ref?: RefObject<View | null>;\n}\n\n// Optional expo-blur dependency - loaded via dynamic import for Metro compatibility\n// Metro can statically analyze import() and will include expo-blur in the bundle if installed\nlet BlurTargetView: ComponentType<BlurTargetViewProps> | null = null;\nlet blurTargetLoadState: 'pending' | 'loaded' | 'failed' = 'pending';\nconst blurTargetLoadListeners: (() => void)[] = [];\n\n// Start loading expo-blur immediately (Metro will bundle it if installed)\nimport('expo-blur')\n .then((mod) => {\n BlurTargetView = mod.BlurTargetView;\n blurTargetLoadState = 'loaded';\n blurTargetLoadListeners.forEach((cb) => cb());\n })\n .catch(() => {\n blurTargetLoadState = 'failed';\n blurTargetLoadListeners.forEach((cb) => cb());\n });\n\n/** Hook to get BlurTargetView component, re-renders when loaded */\nfunction useBlurTargetView() {\n const [, forceUpdate] = useState(0);\n\n useEffect(() => {\n if (blurTargetLoadState === 'pending') {\n const listener = () => forceUpdate((n) => n + 1);\n blurTargetLoadListeners.push(listener);\n return () => {\n const idx = blurTargetLoadListeners.indexOf(listener);\n if (idx >= 0) {\n blurTargetLoadListeners.splice(idx, 1);\n }\n };\n }\n }, []);\n\n return BlurTargetView;\n}\n\ninterface BlurTargetProps {\n children: ReactNode;\n /** Fill the parent container (applies absoluteFill positioning) */\n fill?: boolean;\n style?: ViewStyle;\n}\n\n/**\n * **🔲 Wrapper for content that can be blurred**\n *\n * @description\n * When using blur effects (via the `blur` prop or elevation with blur configured),\n * wrap the content you want to blur in this component and pass its ref to Box's `blurTarget` prop.\n *\n * BlurTarget handles platform differences internally - on iOS it renders children directly,\n * on Android it wraps them in the native view required for blur to work.\n *\n * @example\n * ```tsx\n * import { BlurTarget } from '@yahoo/uds-mobile/BlurTarget';\n * import { Box } from '@yahoo/uds-mobile/Box';\nimport { Text } from '@yahoo/uds-mobile/Text';\n * import { useRef } from 'react';\n * import type { View } from 'react-native';\n *\n * function MyComponent() {\n * const blurTargetRef = useRef<View>(null);\n *\n * return (\n * <Box flex=\"1\">\n * <BlurTarget ref={blurTargetRef} fill>\n * <Image source={backgroundImage} />\n * </BlurTarget>\n *\n * <Box blur={50} blurTarget={blurTargetRef} borderRadius=\"lg\" spacing=\"4\">\n * <Text>Overlay content</Text>\n * </Box>\n * </Box>\n * );\n * }\n * ```\n *\n * @see {@link https://docs.expo.dev/versions/latest/sdk/blur-view/} expo-blur documentation\n */\nconst BlurTarget = forwardRef<View | null, BlurTargetProps>(function BlurTarget(\n { children, fill, style },\n ref,\n) {\n const BlurTargetViewComponent = useBlurTargetView();\n const resolvedStyle = fill ? [StyleSheet.absoluteFill, style] : style;\n\n // On Android with expo-blur, wrap in BlurTargetView for blur to work\n if (Platform.OS === 'android' && BlurTargetViewComponent) {\n return (\n <BlurTargetViewComponent ref={ref as RefObject<View | null>} style={resolvedStyle}>\n {children}\n </BlurTargetViewComponent>\n );\n }\n\n // On iOS (or if expo-blur not installed), wrap in View if style/ref is provided\n // No special BlurTargetView needed - blur works automatically on iOS\n if (resolvedStyle || ref) {\n return (\n <View ref={ref} style={resolvedStyle}>\n {children}\n </View>\n );\n }\n\n // No wrapper needed if no style/ref\n return children;\n});\n\nBlurTarget.displayName = 'BlurTarget';\n\nexport { BlurTarget, type BlurTargetProps };\n"],"mappings":";;;;;AAcA,IAAI,iBAA4D;AAChE,IAAI,sBAAuD;AAC3D,MAAM,0BAA0C,EAAE;AAGlD,OAAO,aACJ,MAAM,QAAQ;CACb,iBAAiB,IAAI;CACrB,sBAAsB;CACtB,wBAAwB,SAAS,OAAO,IAAI,CAAC;EAC7C,CACD,YAAY;CACX,sBAAsB;CACtB,wBAAwB,SAAS,OAAO,IAAI,CAAC;EAC7C;;AAGJ,SAAS,oBAAoB;CAC3B,MAAM,GAAG,eAAe,SAAS,EAAE;CAEnC,gBAAgB;EACd,IAAI,wBAAwB,WAAW;GACrC,MAAM,iBAAiB,aAAa,MAAM,IAAI,EAAE;GAChD,wBAAwB,KAAK,SAAS;GACtC,aAAa;IACX,MAAM,MAAM,wBAAwB,QAAQ,SAAS;IACrD,IAAI,OAAO,GACT,wBAAwB,OAAO,KAAK,EAAE;;;IAI3C,EAAE,CAAC;CAEN,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CT,MAAM,aAAa,WAAyC,SAAS,WACnE,EAAE,UAAU,MAAM,SAClB,KACA;CACA,MAAM,0BAA0B,mBAAmB;CACnD,MAAM,gBAAgB,OAAO,CAAC,WAAW,cAAc,MAAM,GAAG;CAGhE,IAAI,SAAS,OAAO,aAAa,yBAC/B,OACE,oBAAC,yBAAD;EAA8B;EAA+B,OAAO;EACjE;EACuB,CAAA;CAM9B,IAAI,iBAAiB,KACnB,OACE,oBAAC,MAAD;EAAW;EAAK,OAAO;EACpB;EACI,CAAA;CAKX,OAAO;EACP;AAEF,WAAW,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"BottomSheet.js","names":["cornerRadiusLoadState","cornerRadiusListeners","cornerRadiusModule"],"sources":["../../../src/components/BottomSheet/BottomSheet.tsx"],"sourcesContent":["import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { Dimensions, Platform, StyleSheet, View } from 'react-native';\nimport { GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';\nimport Animated, { interpolate, useAnimatedStyle, useSharedValue } from 'react-native-reanimated';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\nimport { FullWindowOverlay } from 'react-native-screens';\n\nimport { bottomSheetStyles } from '../../../generated/styles';\nimport {\n loadListeners as cornerRadiusListeners,\n loadState as cornerRadiusLoadState,\n nativeModule as cornerRadiusModule,\n} from '../../native/UDSScreenCornerRadiusModule';\nimport { Portal } from '../../portal';\nimport { AndroidBackHandler } from '../AndroidBackHandler';\nimport { Box } from '../Box';\nimport { Scrim } from '../Scrim';\nimport { BottomSheetHandle } from './BottomSheetHandle';\nimport { BottomSheetInternalProvider } from './BottomSheetInternalProvider';\nimport { BottomSheetProvider, useBottomSheetContext } from './BottomSheetProvider';\nimport type {\n BottomSheetInternalContextValue,\n BottomSheetProps,\n BottomSheetRootTokens,\n} from './types';\nimport { useBottomSheetDrag } from './useBottomSheetDrag';\nimport { useBottomSheetSnapModel } from './useBottomSheetSnapModel';\nimport { useExpansionMargins } from './useExpansionMargins';\nimport { useKeyboardAvoidance } from './useKeyboardAvoidance';\nimport { resolveHeightToPx } from './utils';\n\nfunction useScreenCornerRadius(): {\n module: typeof cornerRadiusModule;\n isLoaded: boolean;\n isFailed: boolean;\n} {\n const [, forceUpdate] = useState(0);\n\n useEffect(() => {\n if (cornerRadiusLoadState === 'pending') {\n const listener = () => forceUpdate((n) => n + 1);\n cornerRadiusListeners.push(listener);\n return () => {\n const idx = cornerRadiusListeners.indexOf(listener);\n if (idx >= 0) {\n cornerRadiusListeners.splice(idx, 1);\n }\n };\n }\n }, []);\n\n return {\n module: cornerRadiusModule,\n isLoaded: cornerRadiusLoadState === 'loaded',\n isFailed: cornerRadiusLoadState === 'failed',\n };\n}\n\n/**\n * The core bottom sheet component for React Native.\n *\n * Renders a draggable, spring-animated sheet that snaps to configurable height\n * positions. Supports modal (with scrim, blocks background interaction) and\n * non-modal (no scrim, background remains interactive) modes, scroll coordination,\n * keyboard avoidance, and optional edge-to-edge expansion at the max snap point.\n *\n * Must be wrapped in a {@link BottomSheetProvider} or provided a `controller` prop\n * created via {@link useBottomSheetStore}.\n *\n * @example\n * ```tsx\n * const store = useBottomSheetStore();\n *\n * <BottomSheetProvider controller={store}>\n * <BottomSheetTrigger render={<Button>Open</Button>} />\n * <BottomSheet snapPoints={['30%', '80%']}>\n * <BottomSheetHeader>Title</BottomSheetHeader>\n * <BottomSheetContent>...</BottomSheetContent>\n * </BottomSheet>\n * </BottomSheetProvider>\n * ```\n */\nconst BottomSheet = memo(function BottomSheet({\n children,\n controller: controllerProp,\n snapPoints: snapPointsProp,\n height,\n defaultSnapPointIndex = 0,\n snapPointIndex: snapPointIndexProp,\n onSnapPointChange,\n onClose,\n dismissible = true,\n enableDrag = true,\n modal = true,\n showHandleIndicator = true,\n fullWidthAtMaxSnap = false,\n enableOverstretch = true,\n blurTarget,\n shouldMatchDeviceCornerRadius = true,\n}: BottomSheetProps) {\n // Resolve controller: context first, fallback to prop.\n const contextController = useBottomSheetContext();\n const controller = contextController ?? controllerProp;\n\n const { module: screenCornerRadiusModule, isFailed: cornerRadiusFailed } =\n useScreenCornerRadius();\n\n if (!controller) {\n throw new Error(\n 'BottomSheet must be wrapped in BottomSheetProvider or provided a controller prop.',\n );\n }\n\n if (cornerRadiusFailed) {\n throw new Error(\n 'BottomSheet requires `expo-modules-core` to be installed.\\n' +\n 'Install it with: npx expo install expo-modules-core',\n );\n }\n\n const { isOpen } = controller;\n\n // Activate variant styles — must be called in this component so the\n // stylesheet is reactive for all reads below.\n bottomSheetStyles.useVariants({});\n\n const flatRoot = StyleSheet.flatten(bottomSheetStyles.root) as BottomSheetRootTokens;\n const marginHorizontal = flatRoot.marginHorizontal ?? 0;\n const marginBottom = flatRoot.marginBottom ?? 0;\n const blurIntensity = flatRoot.backgroundBlurRadius ?? 0;\n // iOS: real blur via Box BlurView (no target needed).\n // Android: real blur only when a blurTarget is provided.\n // Android fallback: semi-transparent background from the elevation token.\n const showBlurBox = blurIntensity > 0 && (Platform.OS === 'ios' || blurTarget != null);\n const opaqueBackground = flatRoot.backgroundColor ?? '#fff';\n const sheetBackground = showBlurBox\n ? 'transparent'\n : blurIntensity > 0\n ? (flatRoot.backgroundBlurColor ?? opaqueBackground)\n : opaqueBackground;\n\n // When detached (has margins), offset by safe area so the sheet sits above\n // the home indicator rather than overlapping it.\n const { top: safeTop, bottom: safeBottom } = useSafeAreaInsets();\n const isDetached = marginBottom > 0 || marginHorizontal > 0;\n const bottomInsetPx = isDetached ? marginBottom : 0;\n // When fullWidthAtMaxSnap, the sheet extends to the physical screen edge (past safe area).\n const expandedBottomInsetPx = fullWidthAtMaxSnap ? 0 : bottomInsetPx;\n\n // Concentric bottom corner radius: matches the device's screen corner curve.\n // inner radius = screen radius - gap (distance from screen edge to sheet edge).\n const tokenBorderRadius = flatRoot.borderRadius ?? 16;\n const screenCornerRadius = shouldMatchDeviceCornerRadius\n ? (screenCornerRadiusModule?.getCornerRadiusSync() ?? 0)\n : 0;\n const bottomCornerGap = isDetached ? Math.min(marginHorizontal, marginBottom + safeBottom) : 0;\n const bottomCornerRadius =\n screenCornerRadius > 0 ? Math.max(0, screenCornerRadius - bottomCornerGap) : tokenBorderRadius;\n\n // Container height: always use window dimensions (both modal and non-modal render full-screen).\n const containerHeight = useSharedValue(Dimensions.get('window').height);\n\n // Listen for window dimension changes (orientation, resize).\n useEffect(() => {\n const subscription = Dimensions.addEventListener('change', ({ window }) => {\n containerHeight.value = window.height;\n });\n return () => subscription.remove();\n }, [containerHeight]);\n\n // Snap model.\n const snapModel = useBottomSheetSnapModel({\n snapPointsProp,\n height,\n defaultSnapPointIndex,\n snapPointIndexProp,\n onSnapPointChange,\n containerHeight,\n bottomInsetPx,\n topInsetPx: safeTop,\n sideInsetPx: marginHorizontal,\n fullWidthAtMaxSnap,\n expandedBottomInsetPx,\n });\n\n const {\n translateY,\n activeSnapIndex,\n isAnimating,\n resolvedSnapPoints,\n animateToSnap,\n animateToClose,\n animateToSnapWorklet,\n animateToCloseWorklet,\n getClosedTranslateY,\n getTranslateYForSnap,\n } = snapModel;\n\n // Scroll coordination shared values.\n const scrollOffsetY = useSharedValue(0);\n const scrollLocked = useSharedValue(true);\n\n // Dismiss handler: close the controller from the JS thread.\n // Note: onClose is fired from the close effect to avoid double-calling.\n const handleDismiss = useCallback(() => {\n controller.close();\n }, [controller]);\n\n // Drag engine.\n const { panGesture, panGestureRef, openTimeSV } = useBottomSheetDrag({\n enableDrag,\n dismissible,\n enableOverstretch,\n resolvedSnapPoints,\n containerHeight,\n bottomInsetPx,\n translateY,\n activeSnapIndex,\n isAnimating,\n animateToSnapWorklet,\n animateToCloseWorklet,\n onDismiss: handleDismiss,\n scrollLocked,\n scrollOffsetY,\n getTranslateYForSnap,\n });\n\n // Keyboard avoidance — offsets sheet when keyboard opens.\n useKeyboardAvoidance({ isOpen, translateY, isAnimating });\n\n // Expansion margins — animate margins to 0 at max snap.\n const {\n animatedMarginStyle: expansionMarginStyle,\n maxSnapTranslateY: expansionMaxSnapTranslateY,\n thresholdTranslateY: expansionThresholdTranslateY,\n } = useExpansionMargins({\n enabled: fullWidthAtMaxSnap,\n baseMarginHorizontal: marginHorizontal,\n baseMarginBottom: marginBottom,\n resolvedSnapPoints,\n containerHeight,\n bottomInsetPx,\n expandedBottomInsetPx,\n translateY,\n });\n\n // When the sheet extends to the screen edge at max snap, animate the bottom\n // border radius to 0 (physical screen corners handle rounding) and add bottom\n // padding equal to the safe area so content stays above the home indicator.\n const expansionEdgeStyle = useAnimatedStyle(() => {\n if (!fullWidthAtMaxSnap) {\n return {};\n }\n const bottomRadius = interpolate(\n translateY.value,\n [expansionMaxSnapTranslateY, expansionThresholdTranslateY],\n [0, bottomCornerRadius],\n 'clamp',\n );\n const pb = interpolate(\n translateY.value,\n [expansionMaxSnapTranslateY, expansionThresholdTranslateY],\n [safeBottom, 0],\n 'clamp',\n );\n return {\n borderBottomLeftRadius: bottomRadius,\n borderBottomRightRadius: bottomRadius,\n paddingBottom: pb,\n };\n });\n\n // Internal context for BottomSheetContent and useBottomSheetScroll.\n const internalContextValue: BottomSheetInternalContextValue = useMemo(\n () => ({\n panGestureRef,\n activeSnapIndex,\n snapCount: resolvedSnapPoints.length,\n scrollOffsetY,\n scrollLocked,\n }),\n [panGestureRef, activeSnapIndex, resolvedSnapPoints.length, scrollOffsetY, scrollLocked],\n );\n\n // Track previous isOpen to detect transitions.\n const wasOpen = useRef(false);\n // Whether the sheet tree should remain rendered (stays true during close animation).\n const [isRendered, setIsRendered] = useState(false);\n\n useEffect(() => {\n if (isOpen && !wasOpen.current) {\n // Reset scroll state before mounting children so animatedProps reads\n // the correct scrollLocked value on the very first UI frame.\n const initialIndex = snapPointIndexProp ?? defaultSnapPointIndex;\n scrollOffsetY.value = 0;\n scrollLocked.value = initialIndex !== resolvedSnapPoints.length - 1;\n // Opening: mount the tree, then animate to initial snap.\n setIsRendered(true);\n // Start from closed position, then animate in.\n translateY.value = getClosedTranslateY();\n // Set open time for fresh-open lock.\n openTimeSV.value = Date.now();\n // Small delay to ensure layout is ready (mirrors web's 50ms enter delay).\n const timeout = setTimeout(() => {\n animateToSnap(initialIndex);\n }, 50);\n wasOpen.current = true;\n return () => clearTimeout(timeout);\n }\n\n if (!isOpen && wasOpen.current) {\n // Closing: animate to closed, then unmount.\n // If the drag engine already started the close animation, this restarts\n // the spring to the same target — Reanimated cancels the old one (finished=false)\n // and the new one's callback fires when it settles.\n animateToClose(() => {\n setIsRendered(false);\n });\n onClose?.();\n wasOpen.current = false;\n }\n }, [\n isOpen,\n snapPointIndexProp,\n defaultSnapPointIndex,\n translateY,\n getClosedTranslateY,\n animateToSnap,\n animateToClose,\n onClose,\n openTimeSV,\n scrollOffsetY,\n scrollLocked,\n resolvedSnapPoints.length,\n ]);\n\n // Sync controlled snap index changes while open.\n useEffect(() => {\n if (!isOpen || snapPointIndexProp === undefined) {\n return;\n }\n animateToSnap(snapPointIndexProp);\n }, [isOpen, snapPointIndexProp, animateToSnap]);\n\n // Animated sheet style.\n // For detached sheets (with margins), constrain height so the bottom margin\n // is visible. When fullWidthAtMaxSnap, the effective bottom inset interpolates\n // from bottomInsetPx to expandedBottomInsetPx as the sheet reaches max snap.\n const animatedSheetStyle = useAnimatedStyle(() => {\n let effectiveBottom = 0;\n if (isDetached) {\n effectiveBottom = bottomInsetPx;\n if (fullWidthAtMaxSnap) {\n effectiveBottom = interpolate(\n translateY.value,\n [expansionMaxSnapTranslateY, expansionThresholdTranslateY],\n [expandedBottomInsetPx, bottomInsetPx],\n 'clamp',\n );\n }\n }\n return {\n transform: [{ translateY: translateY.value }],\n height: Math.max(100, containerHeight.value - translateY.value - effectiveBottom),\n };\n });\n\n // Compute scrim interpolation positions (JS thread, for Scrim props).\n const initialIndex = snapPointIndexProp ?? defaultSnapPointIndex;\n const windowH = modal ? Dimensions.get('window').height : 0;\n const openSnapHeight = resolvedSnapPoints[initialIndex]\n ? resolveHeightToPx(resolvedSnapPoints[initialIndex], windowH)\n : 0;\n const openTranslateY =\n windowH - Math.min(openSnapHeight, windowH - bottomInsetPx) - bottomInsetPx;\n const closedTranslateY = windowH + bottomInsetPx;\n\n // Don't render anything if not open and close animation has completed.\n if (!isOpen && !isRendered) {\n return null;\n }\n\n const borderWidth = flatRoot.borderWidth ?? 0;\n\n const sheetView = (\n <GestureDetector gesture={panGesture}>\n <Animated.View\n style={[\n internalStyles.sheet,\n {\n backgroundColor: sheetBackground,\n borderTopLeftRadius: tokenBorderRadius,\n borderTopRightRadius: tokenBorderRadius,\n borderBottomLeftRadius: bottomCornerRadius,\n borderBottomRightRadius: bottomCornerRadius,\n paddingTop: 8,\n marginHorizontal,\n overflow: 'hidden',\n },\n animatedSheetStyle,\n fullWidthAtMaxSnap ? expansionMarginStyle : undefined,\n fullWidthAtMaxSnap ? expansionEdgeStyle : undefined,\n ]}\n accessibilityRole=\"none\"\n >\n {showBlurBox && (\n <>\n <Box style={StyleSheet.absoluteFill} blur={blurIntensity} blurTarget={blurTarget} />\n <View\n style={[StyleSheet.absoluteFill, { backgroundColor: flatRoot.backgroundBlurColor }]}\n pointerEvents=\"none\"\n />\n </>\n )}\n {showHandleIndicator && enableDrag && <BottomSheetHandle />}\n <View\n style={{\n paddingHorizontal: flatRoot.paddingHorizontal ?? 16,\n paddingBottom: flatRoot.paddingVertical ?? 8,\n flex: 1,\n }}\n >\n <BottomSheetInternalProvider value={internalContextValue}>\n {children}\n </BottomSheetInternalProvider>\n </View>\n {borderWidth > 0 && (\n <Animated.View\n style={[\n StyleSheet.absoluteFill,\n {\n borderColor: flatRoot.borderColor ?? 'transparent',\n borderWidth,\n borderTopLeftRadius: tokenBorderRadius,\n borderTopRightRadius: tokenBorderRadius,\n borderBottomLeftRadius: bottomCornerRadius,\n borderBottomRightRadius: bottomCornerRadius,\n },\n fullWidthAtMaxSnap ? expansionEdgeStyle : undefined,\n ]}\n pointerEvents=\"none\"\n />\n )}\n </Animated.View>\n </GestureDetector>\n );\n\n // Modal: full-screen gesture root with scrim blocks background interaction.\n const modalOverlay = (\n <GestureHandlerRootView\n style={internalStyles.modalRoot}\n pointerEvents={isOpen ? 'auto' : 'none'}\n >\n <Scrim\n translateY={translateY}\n openTranslateY={openTranslateY}\n closedTranslateY={closedTranslateY}\n dismissible={dismissible}\n onDismiss={handleDismiss}\n blurTarget={blurTarget}\n />\n {sheetView}\n </GestureHandlerRootView>\n );\n\n // Non-modal: gesture root wraps only the sheet so background stays interactive.\n const nonModalOverlay = (\n <View style={StyleSheet.absoluteFill} pointerEvents={isOpen ? 'box-none' : 'none'}>\n <GestureHandlerRootView style={internalStyles.sheet} pointerEvents=\"auto\">\n {sheetView}\n </GestureHandlerRootView>\n </View>\n );\n\n // iOS: FullWindowOverlay renders above all content at the window level.\n if (Platform.OS === 'ios') {\n return <FullWindowOverlay>{modal ? modalOverlay : nonModalOverlay}</FullWindowOverlay>;\n }\n\n // Android: Portal renders above all content via PortalHost. Re-provides\n // BottomSheetProvider since Portal re-parents outside the original tree.\n return (\n <Portal>\n <BottomSheetProvider controller={controller}>\n {modal && (\n <AndroidBackHandler dismissible={dismissible} onDismiss={handleDismiss} isOpen={isOpen} />\n )}\n {modal ? modalOverlay : nonModalOverlay}\n </BottomSheetProvider>\n </Portal>\n );\n});\n\nconst internalStyles = StyleSheet.create({\n modalRoot: {\n flex: 1,\n },\n sheet: {\n position: 'absolute',\n left: 0,\n right: 0,\n top: 0,\n minHeight: 100,\n },\n});\n\nexport { BottomSheet };\n"],"mappings":"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"}
1
+ {"version":3,"file":"BottomSheet.js","names":["cornerRadiusLoadState","cornerRadiusListeners","cornerRadiusModule"],"sources":["../../../src/components/BottomSheet/BottomSheet.tsx"],"sourcesContent":["import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { Dimensions, Platform, StyleSheet, View } from 'react-native';\nimport { GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';\nimport Animated, { interpolate, useAnimatedStyle, useSharedValue } from 'react-native-reanimated';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\nimport { FullWindowOverlay } from 'react-native-screens';\n\nimport { bottomSheetStyles } from '../../../generated/styles';\nimport {\n loadListeners as cornerRadiusListeners,\n loadState as cornerRadiusLoadState,\n nativeModule as cornerRadiusModule,\n} from '../../native/UDSScreenCornerRadiusModule';\nimport { Portal } from '../../portal';\nimport { AndroidBackHandler } from '../AndroidBackHandler';\nimport { Box } from '../Box';\nimport { Scrim } from '../Scrim';\nimport { BottomSheetHandle } from './BottomSheetHandle';\nimport { BottomSheetInternalProvider } from './BottomSheetInternalProvider';\nimport { BottomSheetProvider, useBottomSheetContext } from './BottomSheetProvider';\nimport type {\n BottomSheetInternalContextValue,\n BottomSheetProps,\n BottomSheetRootTokens,\n} from './types';\nimport { useBottomSheetDrag } from './useBottomSheetDrag';\nimport { useBottomSheetSnapModel } from './useBottomSheetSnapModel';\nimport { useExpansionMargins } from './useExpansionMargins';\nimport { useKeyboardAvoidance } from './useKeyboardAvoidance';\nimport { resolveHeightToPx } from './utils';\n\nfunction useScreenCornerRadius(): {\n module: typeof cornerRadiusModule;\n isLoaded: boolean;\n isFailed: boolean;\n} {\n const [, forceUpdate] = useState(0);\n\n useEffect(() => {\n if (cornerRadiusLoadState === 'pending') {\n const listener = () => forceUpdate((n) => n + 1);\n cornerRadiusListeners.push(listener);\n return () => {\n const idx = cornerRadiusListeners.indexOf(listener);\n if (idx >= 0) {\n cornerRadiusListeners.splice(idx, 1);\n }\n };\n }\n }, []);\n\n return {\n module: cornerRadiusModule,\n isLoaded: cornerRadiusLoadState === 'loaded',\n isFailed: cornerRadiusLoadState === 'failed',\n };\n}\n\n/**\n * The core bottom sheet component for React Native.\n *\n * Renders a draggable, spring-animated sheet that snaps to configurable height\n * positions. Supports modal (with scrim, blocks background interaction) and\n * non-modal (no scrim, background remains interactive) modes, scroll coordination,\n * keyboard avoidance, and optional edge-to-edge expansion at the max snap point.\n *\n * Must be wrapped in a {@link BottomSheetProvider} or provided a `controller` prop\n * created via {@link useBottomSheetStore}.\n *\n * @example\n * ```tsx\n * const store = useBottomSheetStore();\n *\n * <BottomSheetProvider controller={store}>\n * <BottomSheetTrigger render={<Button>Open</Button>} />\n * <BottomSheet snapPoints={['30%', '80%']}>\n * <BottomSheetHeader>Title</BottomSheetHeader>\n * <BottomSheetContent>...</BottomSheetContent>\n * </BottomSheet>\n * </BottomSheetProvider>\n * ```\n */\nconst BottomSheet = memo(function BottomSheet({\n children,\n controller: controllerProp,\n snapPoints: snapPointsProp,\n height,\n defaultSnapPointIndex = 0,\n snapPointIndex: snapPointIndexProp,\n onSnapPointChange,\n onClose,\n dismissible = true,\n enableDrag = true,\n modal = true,\n showHandleIndicator = true,\n fullWidthAtMaxSnap = false,\n enableOverstretch = true,\n blurTarget,\n shouldMatchDeviceCornerRadius = true,\n}: BottomSheetProps) {\n // Resolve controller: context first, fallback to prop.\n const contextController = useBottomSheetContext();\n const controller = contextController ?? controllerProp;\n\n const { module: screenCornerRadiusModule, isFailed: cornerRadiusFailed } =\n useScreenCornerRadius();\n\n if (!controller) {\n throw new Error(\n 'BottomSheet must be wrapped in BottomSheetProvider or provided a controller prop.',\n );\n }\n\n if (cornerRadiusFailed) {\n throw new Error(\n 'BottomSheet requires `expo-modules-core` to be installed.\\n' +\n 'Install it with: npx expo install expo-modules-core',\n );\n }\n\n const { isOpen } = controller;\n\n // Activate variant styles — must be called in this component so the\n // stylesheet is reactive for all reads below.\n bottomSheetStyles.useVariants({});\n\n const flatRoot = StyleSheet.flatten(bottomSheetStyles.root) as BottomSheetRootTokens;\n const marginHorizontal = flatRoot.marginHorizontal ?? 0;\n const marginBottom = flatRoot.marginBottom ?? 0;\n const blurIntensity = flatRoot.backgroundBlurRadius ?? 0;\n // iOS: real blur via Box BlurView (no target needed).\n // Android: real blur only when a blurTarget is provided.\n // Android fallback: semi-transparent background from the elevation token.\n const showBlurBox = blurIntensity > 0 && (Platform.OS === 'ios' || blurTarget != null);\n const opaqueBackground = flatRoot.backgroundColor ?? '#fff';\n const sheetBackground = showBlurBox\n ? 'transparent'\n : blurIntensity > 0\n ? (flatRoot.backgroundBlurColor ?? opaqueBackground)\n : opaqueBackground;\n\n // When detached (has margins), offset by safe area so the sheet sits above\n // the home indicator rather than overlapping it.\n const { top: safeTop, bottom: safeBottom } = useSafeAreaInsets();\n const isDetached = marginBottom > 0 || marginHorizontal > 0;\n const bottomInsetPx = isDetached ? marginBottom : 0;\n // When fullWidthAtMaxSnap, the sheet extends to the physical screen edge (past safe area).\n const expandedBottomInsetPx = fullWidthAtMaxSnap ? 0 : bottomInsetPx;\n\n // Concentric bottom corner radius: matches the device's screen corner curve.\n // inner radius = screen radius - gap (distance from screen edge to sheet edge).\n const tokenBorderRadius = flatRoot.borderRadius ?? 16;\n const screenCornerRadius = shouldMatchDeviceCornerRadius\n ? (screenCornerRadiusModule?.getCornerRadiusSync() ?? 0)\n : 0;\n const bottomCornerGap = isDetached ? Math.min(marginHorizontal, marginBottom + safeBottom) : 0;\n const bottomCornerRadius =\n screenCornerRadius > 0 ? Math.max(0, screenCornerRadius - bottomCornerGap) : tokenBorderRadius;\n\n // Container height: always use window dimensions (both modal and non-modal render full-screen).\n const containerHeight = useSharedValue(Dimensions.get('window').height);\n\n // Listen for window dimension changes (orientation, resize).\n useEffect(() => {\n const subscription = Dimensions.addEventListener('change', ({ window }) => {\n containerHeight.value = window.height;\n });\n return () => subscription.remove();\n }, [containerHeight]);\n\n // Snap model.\n const snapModel = useBottomSheetSnapModel({\n snapPointsProp,\n height,\n defaultSnapPointIndex,\n snapPointIndexProp,\n onSnapPointChange,\n containerHeight,\n bottomInsetPx,\n topInsetPx: safeTop,\n sideInsetPx: marginHorizontal,\n fullWidthAtMaxSnap,\n expandedBottomInsetPx,\n });\n\n const {\n translateY,\n activeSnapIndex,\n isAnimating,\n resolvedSnapPoints,\n animateToSnap,\n animateToClose,\n animateToSnapWorklet,\n animateToCloseWorklet,\n getClosedTranslateY,\n getTranslateYForSnap,\n } = snapModel;\n\n // Scroll coordination shared values.\n const scrollOffsetY = useSharedValue(0);\n const scrollLocked = useSharedValue(true);\n\n // Dismiss handler: close the controller from the JS thread.\n // Note: onClose is fired from the close effect to avoid double-calling.\n const handleDismiss = useCallback(() => {\n controller.close();\n }, [controller]);\n\n // Drag engine.\n const { panGesture, panGestureRef, openTimeSV } = useBottomSheetDrag({\n enableDrag,\n dismissible,\n enableOverstretch,\n resolvedSnapPoints,\n containerHeight,\n bottomInsetPx,\n translateY,\n activeSnapIndex,\n isAnimating,\n animateToSnapWorklet,\n animateToCloseWorklet,\n onDismiss: handleDismiss,\n scrollLocked,\n scrollOffsetY,\n getTranslateYForSnap,\n });\n\n // Keyboard avoidance — offsets sheet when keyboard opens.\n useKeyboardAvoidance({ isOpen, translateY, isAnimating });\n\n // Expansion margins — animate margins to 0 at max snap.\n const {\n animatedMarginStyle: expansionMarginStyle,\n maxSnapTranslateY: expansionMaxSnapTranslateY,\n thresholdTranslateY: expansionThresholdTranslateY,\n } = useExpansionMargins({\n enabled: fullWidthAtMaxSnap,\n baseMarginHorizontal: marginHorizontal,\n baseMarginBottom: marginBottom,\n resolvedSnapPoints,\n containerHeight,\n bottomInsetPx,\n expandedBottomInsetPx,\n translateY,\n });\n\n // When the sheet extends to the screen edge at max snap, animate the bottom\n // border radius to 0 (physical screen corners handle rounding) and add bottom\n // padding equal to the safe area so content stays above the home indicator.\n const expansionEdgeStyle = useAnimatedStyle(() => {\n if (!fullWidthAtMaxSnap) {\n return {};\n }\n const bottomRadius = interpolate(\n translateY.value,\n [expansionMaxSnapTranslateY, expansionThresholdTranslateY],\n [0, bottomCornerRadius],\n 'clamp',\n );\n const pb = interpolate(\n translateY.value,\n [expansionMaxSnapTranslateY, expansionThresholdTranslateY],\n [safeBottom, 0],\n 'clamp',\n );\n return {\n borderBottomLeftRadius: bottomRadius,\n borderBottomRightRadius: bottomRadius,\n paddingBottom: pb,\n };\n });\n\n // Internal context for BottomSheetContent and useBottomSheetScroll.\n const internalContextValue: BottomSheetInternalContextValue = useMemo(\n () => ({\n panGestureRef,\n activeSnapIndex,\n snapCount: resolvedSnapPoints.length,\n scrollOffsetY,\n scrollLocked,\n }),\n [panGestureRef, activeSnapIndex, resolvedSnapPoints.length, scrollOffsetY, scrollLocked],\n );\n\n // Track previous isOpen to detect transitions.\n const wasOpen = useRef(false);\n // Whether the sheet tree should remain rendered (stays true during close animation).\n const [isRendered, setIsRendered] = useState(false);\n\n useEffect(() => {\n if (isOpen && !wasOpen.current) {\n // Reset scroll state before mounting children so animatedProps reads\n // the correct scrollLocked value on the very first UI frame.\n const initialIndex = snapPointIndexProp ?? defaultSnapPointIndex;\n scrollOffsetY.value = 0;\n scrollLocked.value = initialIndex !== resolvedSnapPoints.length - 1;\n // Opening: mount the tree, then animate to initial snap.\n setIsRendered(true);\n // Start from closed position, then animate in.\n translateY.value = getClosedTranslateY();\n // Set open time for fresh-open lock.\n openTimeSV.value = Date.now();\n // Small delay to ensure layout is ready (mirrors web's 50ms enter delay).\n const timeout = setTimeout(() => {\n animateToSnap(initialIndex);\n }, 50);\n wasOpen.current = true;\n return () => clearTimeout(timeout);\n }\n\n if (!isOpen && wasOpen.current) {\n // Closing: animate to closed, then unmount.\n // If the drag engine already started the close animation, this restarts\n // the spring to the same target — Reanimated cancels the old one (finished=false)\n // and the new one's callback fires when it settles.\n animateToClose(() => {\n setIsRendered(false);\n });\n onClose?.();\n wasOpen.current = false;\n }\n }, [\n isOpen,\n snapPointIndexProp,\n defaultSnapPointIndex,\n translateY,\n getClosedTranslateY,\n animateToSnap,\n animateToClose,\n onClose,\n openTimeSV,\n scrollOffsetY,\n scrollLocked,\n resolvedSnapPoints.length,\n ]);\n\n // Sync controlled snap index changes while open.\n useEffect(() => {\n if (!isOpen || snapPointIndexProp === undefined) {\n return;\n }\n animateToSnap(snapPointIndexProp);\n }, [isOpen, snapPointIndexProp, animateToSnap]);\n\n // Animated sheet style.\n // For detached sheets (with margins), constrain height so the bottom margin\n // is visible. When fullWidthAtMaxSnap, the effective bottom inset interpolates\n // from bottomInsetPx to expandedBottomInsetPx as the sheet reaches max snap.\n const animatedSheetStyle = useAnimatedStyle(() => {\n let effectiveBottom = 0;\n if (isDetached) {\n effectiveBottom = bottomInsetPx;\n if (fullWidthAtMaxSnap) {\n effectiveBottom = interpolate(\n translateY.value,\n [expansionMaxSnapTranslateY, expansionThresholdTranslateY],\n [expandedBottomInsetPx, bottomInsetPx],\n 'clamp',\n );\n }\n }\n return {\n transform: [{ translateY: translateY.value }],\n height: Math.max(100, containerHeight.value - translateY.value - effectiveBottom),\n };\n });\n\n // Compute scrim interpolation positions (JS thread, for Scrim props).\n const initialIndex = snapPointIndexProp ?? defaultSnapPointIndex;\n const windowH = modal ? Dimensions.get('window').height : 0;\n const openSnapHeight = resolvedSnapPoints[initialIndex]\n ? resolveHeightToPx(resolvedSnapPoints[initialIndex], windowH)\n : 0;\n const openTranslateY =\n windowH - Math.min(openSnapHeight, windowH - bottomInsetPx) - bottomInsetPx;\n const closedTranslateY = windowH + bottomInsetPx;\n\n // Don't render anything if not open and close animation has completed.\n if (!isOpen && !isRendered) {\n return null;\n }\n\n const borderWidth = flatRoot.borderWidth ?? 0;\n\n const sheetView = (\n <GestureDetector gesture={panGesture}>\n <Animated.View\n style={[\n internalStyles.sheet,\n {\n backgroundColor: sheetBackground,\n borderTopLeftRadius: tokenBorderRadius,\n borderTopRightRadius: tokenBorderRadius,\n borderBottomLeftRadius: bottomCornerRadius,\n borderBottomRightRadius: bottomCornerRadius,\n paddingTop: 8,\n marginHorizontal,\n overflow: 'hidden',\n },\n animatedSheetStyle,\n fullWidthAtMaxSnap ? expansionMarginStyle : undefined,\n fullWidthAtMaxSnap ? expansionEdgeStyle : undefined,\n ]}\n accessibilityRole=\"none\"\n >\n {showBlurBox && (\n <>\n <Box style={StyleSheet.absoluteFill} blur={blurIntensity} blurTarget={blurTarget} />\n <View\n style={[StyleSheet.absoluteFill, { backgroundColor: flatRoot.backgroundBlurColor }]}\n pointerEvents=\"none\"\n />\n </>\n )}\n {showHandleIndicator && enableDrag && <BottomSheetHandle />}\n <View\n style={{\n paddingHorizontal: flatRoot.paddingHorizontal ?? 16,\n paddingBottom: flatRoot.paddingVertical ?? 8,\n flex: 1,\n }}\n >\n <BottomSheetInternalProvider value={internalContextValue}>\n {children}\n </BottomSheetInternalProvider>\n </View>\n {borderWidth > 0 && (\n <Animated.View\n style={[\n StyleSheet.absoluteFill,\n {\n borderColor: flatRoot.borderColor ?? 'transparent',\n borderWidth,\n borderTopLeftRadius: tokenBorderRadius,\n borderTopRightRadius: tokenBorderRadius,\n borderBottomLeftRadius: bottomCornerRadius,\n borderBottomRightRadius: bottomCornerRadius,\n },\n fullWidthAtMaxSnap ? expansionEdgeStyle : undefined,\n ]}\n pointerEvents=\"none\"\n />\n )}\n </Animated.View>\n </GestureDetector>\n );\n\n // Modal: full-screen gesture root with scrim blocks background interaction.\n const modalOverlay = (\n <GestureHandlerRootView\n style={internalStyles.modalRoot}\n pointerEvents={isOpen ? 'auto' : 'none'}\n >\n <Scrim\n translateY={translateY}\n openTranslateY={openTranslateY}\n closedTranslateY={closedTranslateY}\n dismissible={dismissible}\n onDismiss={handleDismiss}\n blurTarget={blurTarget}\n />\n {sheetView}\n </GestureHandlerRootView>\n );\n\n // Non-modal: gesture root wraps only the sheet so background stays interactive.\n const nonModalOverlay = (\n <View style={StyleSheet.absoluteFill} pointerEvents={isOpen ? 'box-none' : 'none'}>\n <GestureHandlerRootView style={internalStyles.sheet} pointerEvents=\"auto\">\n {sheetView}\n </GestureHandlerRootView>\n </View>\n );\n\n // iOS: FullWindowOverlay renders above all content at the window level.\n if (Platform.OS === 'ios') {\n return <FullWindowOverlay>{modal ? modalOverlay : nonModalOverlay}</FullWindowOverlay>;\n }\n\n // Android: Portal renders above all content via PortalHost. Re-provides\n // BottomSheetProvider since Portal re-parents outside the original tree.\n return (\n <Portal>\n <BottomSheetProvider controller={controller}>\n {modal && (\n <AndroidBackHandler dismissible={dismissible} onDismiss={handleDismiss} isOpen={isOpen} />\n )}\n {modal ? modalOverlay : nonModalOverlay}\n </BottomSheetProvider>\n </Portal>\n );\n});\n\nconst internalStyles = StyleSheet.create({\n modalRoot: {\n flex: 1,\n },\n sheet: {\n position: 'absolute',\n left: 0,\n right: 0,\n top: 0,\n minHeight: 100,\n },\n});\n\nexport { BottomSheet };\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"BottomSheetContent.js","names":[],"sources":["../../../src/components/BottomSheet/BottomSheetContent.tsx"],"sourcesContent":["import { memo, useMemo } from 'react';\nimport { Platform, StyleSheet } from 'react-native';\nimport { Gesture, GestureDetector } from 'react-native-gesture-handler';\nimport Animated, {\n useAnimatedProps,\n useAnimatedRef,\n useAnimatedScrollHandler,\n} from 'react-native-reanimated';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\n\nimport { useBottomSheetInternalContext } from './BottomSheetInternalProvider';\nimport type { BottomSheetContentProps } from './types';\n\n/**\n * Scrollable content area for the bottom sheet.\n *\n * Renders an `Animated.ScrollView` that coordinates with the sheet's pan gesture:\n *\n * 1. `scrollEnabled` disables touch handling when locked; `decelerationRate`\n * prevents residual momentum.\n * 2. The scroll handler tracks `contentOffset.y` for the drag gesture.\n * 3. A `Gesture.Native()` links to the sheet's pan gesture via\n * `simultaneousWithExternalGesture` for gesture handoff.\n *\n * Automatically adds bottom safe-area padding to the content container.\n */\nconst BottomSheetContent = memo(function BottomSheetContent({ children }: BottomSheetContentProps) {\n const internalCtx = useBottomSheetInternalContext();\n const scrollRef = useAnimatedRef<Animated.ScrollView>();\n const { bottom: safeBottom } = useSafeAreaInsets();\n\n const contentContainerStyle = useMemo(() => ({ paddingBottom: safeBottom }), [safeBottom]);\n\n const animatedScrollProps = useAnimatedProps(() => ({\n scrollEnabled: !internalCtx?.scrollLocked.value,\n decelerationRate: internalCtx?.scrollLocked.value ? 0 : 0.998,\n }));\n\n // Track scroll offset so the drag gesture knows the scroll position.\n const scrollHandler = useAnimatedScrollHandler({\n onScroll: (event) => {\n if (!internalCtx) {\n return;\n }\n internalCtx.scrollOffsetY.value = event.contentOffset.y;\n },\n });\n\n if (!internalCtx) {\n return (\n <Animated.ScrollView\n style={internalStyles.container}\n contentContainerStyle={contentContainerStyle}\n >\n {children}\n </Animated.ScrollView>\n );\n }\n\n const { panGestureRef } = internalCtx;\n\n const nativeGesture = Gesture.Native().simultaneousWithExternalGesture(panGestureRef);\n\n return (\n <GestureDetector gesture={nativeGesture}>\n <Animated.ScrollView\n ref={scrollRef}\n style={internalStyles.container}\n contentContainerStyle={contentContainerStyle}\n onScroll={scrollHandler}\n scrollEventThrottle={16}\n animatedProps={animatedScrollProps}\n bounces={false}\n overScrollMode=\"never\"\n nestedScrollEnabled={Platform.OS === 'android'}\n >\n {children}\n </Animated.ScrollView>\n </GestureDetector>\n );\n});\n\nconst internalStyles = StyleSheet.create({\n container: {\n flex: 1,\n },\n});\n\nexport { BottomSheetContent };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAM,qBAAqB,KAAK,SAAS,mBAAmB,EAAE,YAAqC;CACjG,MAAM,cAAc,+BAA+B;CACnD,MAAM,YAAY,gBAAqC;CACvD,MAAM,EAAE,QAAQ,eAAe,mBAAmB;CAElD,MAAM,wBAAwB,eAAe,EAAE,eAAe,YAAY,GAAG,CAAC,WAAW,CAAC;CAE1F,MAAM,sBAAsB,wBAAwB;EAClD,eAAe,CAAC,aAAa,aAAa;EAC1C,kBAAkB,aAAa,aAAa,QAAQ,IAAI;EACzD,EAAE;CAGH,MAAM,gBAAgB,yBAAyB,EAC7C,WAAW,UAAU;AACnB,MAAI,CAAC,YACH;AAEF,cAAY,cAAc,QAAQ,MAAM,cAAc;IAEzD,CAAC;AAEF,KAAI,CAAC,YACH,QACE,oBAAC,SAAS,YAAV;EACE,OAAO,eAAe;EACC;EAEtB;EACmB,CAAA;CAI1B,MAAM,EAAE,kBAAkB;AAI1B,QACE,oBAAC,iBAAD;EAAiB,SAHG,QAAQ,QAAQ,CAAC,gCAAgC,cAG9B;YACrC,oBAAC,SAAS,YAAV;GACE,KAAK;GACL,OAAO,eAAe;GACC;GACvB,UAAU;GACV,qBAAqB;GACrB,eAAe;GACf,SAAS;GACT,gBAAe;GACf,qBAAqB,SAAS,OAAO;GAEpC;GACmB,CAAA;EACN,CAAA;EAEpB;AAEF,MAAM,iBAAiB,WAAW,OAAO,EACvC,WAAW,EACT,MAAM,GACP,EACF,CAAC"}
1
+ {"version":3,"file":"BottomSheetContent.js","names":[],"sources":["../../../src/components/BottomSheet/BottomSheetContent.tsx"],"sourcesContent":["import { memo, useMemo } from 'react';\nimport { Platform, StyleSheet } from 'react-native';\nimport { Gesture, GestureDetector } from 'react-native-gesture-handler';\nimport Animated, {\n useAnimatedProps,\n useAnimatedRef,\n useAnimatedScrollHandler,\n} from 'react-native-reanimated';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\n\nimport { useBottomSheetInternalContext } from './BottomSheetInternalProvider';\nimport type { BottomSheetContentProps } from './types';\n\n/**\n * Scrollable content area for the bottom sheet.\n *\n * Renders an `Animated.ScrollView` that coordinates with the sheet's pan gesture:\n *\n * 1. `scrollEnabled` disables touch handling when locked; `decelerationRate`\n * prevents residual momentum.\n * 2. The scroll handler tracks `contentOffset.y` for the drag gesture.\n * 3. A `Gesture.Native()` links to the sheet's pan gesture via\n * `simultaneousWithExternalGesture` for gesture handoff.\n *\n * Automatically adds bottom safe-area padding to the content container.\n */\nconst BottomSheetContent = memo(function BottomSheetContent({ children }: BottomSheetContentProps) {\n const internalCtx = useBottomSheetInternalContext();\n const scrollRef = useAnimatedRef<Animated.ScrollView>();\n const { bottom: safeBottom } = useSafeAreaInsets();\n\n const contentContainerStyle = useMemo(() => ({ paddingBottom: safeBottom }), [safeBottom]);\n\n const animatedScrollProps = useAnimatedProps(() => ({\n scrollEnabled: !internalCtx?.scrollLocked.value,\n decelerationRate: internalCtx?.scrollLocked.value ? 0 : 0.998,\n }));\n\n // Track scroll offset so the drag gesture knows the scroll position.\n const scrollHandler = useAnimatedScrollHandler({\n onScroll: (event) => {\n if (!internalCtx) {\n return;\n }\n internalCtx.scrollOffsetY.value = event.contentOffset.y;\n },\n });\n\n if (!internalCtx) {\n return (\n <Animated.ScrollView\n style={internalStyles.container}\n contentContainerStyle={contentContainerStyle}\n >\n {children}\n </Animated.ScrollView>\n );\n }\n\n const { panGestureRef } = internalCtx;\n\n const nativeGesture = Gesture.Native().simultaneousWithExternalGesture(panGestureRef);\n\n return (\n <GestureDetector gesture={nativeGesture}>\n <Animated.ScrollView\n ref={scrollRef}\n style={internalStyles.container}\n contentContainerStyle={contentContainerStyle}\n onScroll={scrollHandler}\n scrollEventThrottle={16}\n animatedProps={animatedScrollProps}\n bounces={false}\n overScrollMode=\"never\"\n nestedScrollEnabled={Platform.OS === 'android'}\n >\n {children}\n </Animated.ScrollView>\n </GestureDetector>\n );\n});\n\nconst internalStyles = StyleSheet.create({\n container: {\n flex: 1,\n },\n});\n\nexport { BottomSheetContent };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAM,qBAAqB,KAAK,SAAS,mBAAmB,EAAE,YAAqC;CACjG,MAAM,cAAc,+BAA+B;CACnD,MAAM,YAAY,gBAAqC;CACvD,MAAM,EAAE,QAAQ,eAAe,mBAAmB;CAElD,MAAM,wBAAwB,eAAe,EAAE,eAAe,YAAY,GAAG,CAAC,WAAW,CAAC;CAE1F,MAAM,sBAAsB,wBAAwB;EAClD,eAAe,CAAC,aAAa,aAAa;EAC1C,kBAAkB,aAAa,aAAa,QAAQ,IAAI;EACzD,EAAE;CAGH,MAAM,gBAAgB,yBAAyB,EAC7C,WAAW,UAAU;EACnB,IAAI,CAAC,aACH;EAEF,YAAY,cAAc,QAAQ,MAAM,cAAc;IAEzD,CAAC;CAEF,IAAI,CAAC,aACH,OACE,oBAAC,SAAS,YAAV;EACE,OAAO,eAAe;EACC;EAEtB;EACmB,CAAA;CAI1B,MAAM,EAAE,kBAAkB;CAI1B,OACE,oBAAC,iBAAD;EAAiB,SAHG,QAAQ,QAAQ,CAAC,gCAAgC,cAG9B;YACrC,oBAAC,SAAS,YAAV;GACE,KAAK;GACL,OAAO,eAAe;GACC;GACvB,UAAU;GACV,qBAAqB;GACrB,eAAe;GACf,SAAS;GACT,gBAAe;GACf,qBAAqB,SAAS,OAAO;GAEpC;GACmB,CAAA;EACN,CAAA;EAEpB;AAEF,MAAM,iBAAiB,WAAW,OAAO,EACvC,WAAW,EACT,MAAM,GACP,EACF,CAAC"}