react-native-molecules 0.5.0-beta.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 (355) hide show
  1. package/components/Accordion/Accordion.tsx +89 -0
  2. package/components/Accordion/AccordionItem.tsx +94 -0
  3. package/components/Accordion/AccordionItemContent.tsx +17 -0
  4. package/components/Accordion/AccordionItemHeader.tsx +122 -0
  5. package/components/Accordion/index.tsx +36 -0
  6. package/components/Accordion/utils.ts +62 -0
  7. package/components/ActivityIndicator/ActivityIndicator.ios.tsx +1 -0
  8. package/components/ActivityIndicator/ActivityIndicator.tsx +200 -0
  9. package/components/ActivityIndicator/AnimatedSpinner.tsx +121 -0
  10. package/components/ActivityIndicator/index.tsx +17 -0
  11. package/components/Appbar/AppbarActions.tsx +13 -0
  12. package/components/Appbar/AppbarBase.tsx +60 -0
  13. package/components/Appbar/AppbarCenterAligned.tsx +13 -0
  14. package/components/Appbar/AppbarLarge.tsx +13 -0
  15. package/components/Appbar/AppbarLeft.tsx +26 -0
  16. package/components/Appbar/AppbarMedium.tsx +13 -0
  17. package/components/Appbar/AppbarRight.tsx +40 -0
  18. package/components/Appbar/AppbarSmall.tsx +13 -0
  19. package/components/Appbar/AppbarTitle.tsx +49 -0
  20. package/components/Appbar/index.tsx +46 -0
  21. package/components/Appbar/types.ts +19 -0
  22. package/components/Appbar/utils.ts +127 -0
  23. package/components/Avatar/Avatar.tsx +189 -0
  24. package/components/Avatar/index.tsx +11 -0
  25. package/components/Avatar/utils.ts +35 -0
  26. package/components/Backdrop/Backdrop.tsx +18 -0
  27. package/components/Backdrop/index.tsx +11 -0
  28. package/components/Backdrop/types.ts +3 -0
  29. package/components/Backdrop/utils.ts +21 -0
  30. package/components/Badge/Badge.tsx +29 -0
  31. package/components/Badge/index.tsx +11 -0
  32. package/components/Badge/utils.ts +38 -0
  33. package/components/Button/Button.tsx +349 -0
  34. package/components/Button/index.tsx +11 -0
  35. package/components/Button/types.ts +5 -0
  36. package/components/Button/utils.ts +416 -0
  37. package/components/Card/Card.tsx +160 -0
  38. package/components/Card/CardActions.tsx +18 -0
  39. package/components/Card/CardContent.tsx +29 -0
  40. package/components/Card/CardHeader.tsx +31 -0
  41. package/components/Card/CardHeadline.tsx +15 -0
  42. package/components/Card/CardMedia.tsx +36 -0
  43. package/components/Card/CardSubhead.tsx +15 -0
  44. package/components/Card/CardText.tsx +13 -0
  45. package/components/Card/CardTypography.tsx +113 -0
  46. package/components/Card/index.tsx +38 -0
  47. package/components/Card/types.ts +1 -0
  48. package/components/Card/utils.ts +23 -0
  49. package/components/Checkbox/Checkbox.tsx +123 -0
  50. package/components/Checkbox/CheckboxBase.ios.tsx +86 -0
  51. package/components/Checkbox/CheckboxBase.tsx +216 -0
  52. package/components/Checkbox/index.tsx +11 -0
  53. package/components/Checkbox/types.ts +72 -0
  54. package/components/Checkbox/utils.ts +210 -0
  55. package/components/Chip/Chip.tsx +416 -0
  56. package/components/Chip/index.tsx +51 -0
  57. package/components/Chip/utils.ts +100 -0
  58. package/components/DatePickerDocked/DatePickerDocked.tsx +30 -0
  59. package/components/DatePickerDocked/DatePickerDockedHeader.tsx +129 -0
  60. package/components/DatePickerDocked/index.tsx +21 -0
  61. package/components/DatePickerDocked/types.ts +11 -0
  62. package/components/DatePickerDocked/utils.ts +155 -0
  63. package/components/DatePickerInline/AutoSizer.tsx +46 -0
  64. package/components/DatePickerInline/DatePickerContext.tsx +21 -0
  65. package/components/DatePickerInline/DatePickerInline.tsx +82 -0
  66. package/components/DatePickerInline/DatePickerInlineBase.tsx +181 -0
  67. package/components/DatePickerInline/DatePickerInlineHeader.tsx +108 -0
  68. package/components/DatePickerInline/Day.tsx +88 -0
  69. package/components/DatePickerInline/DayName.tsx +17 -0
  70. package/components/DatePickerInline/DayNames.tsx +32 -0
  71. package/components/DatePickerInline/DayRange.tsx +48 -0
  72. package/components/DatePickerInline/HeaderItem.tsx +111 -0
  73. package/components/DatePickerInline/Month.tsx +233 -0
  74. package/components/DatePickerInline/MonthPicker.tsx +174 -0
  75. package/components/DatePickerInline/Swiper.native.tsx +172 -0
  76. package/components/DatePickerInline/Swiper.tsx +172 -0
  77. package/components/DatePickerInline/SwiperUtils.ts +40 -0
  78. package/components/DatePickerInline/Week.tsx +67 -0
  79. package/components/DatePickerInline/YearPicker.tsx +139 -0
  80. package/components/DatePickerInline/dateUtils.tsx +334 -0
  81. package/components/DatePickerInline/index.tsx +41 -0
  82. package/components/DatePickerInline/types.ts +104 -0
  83. package/components/DatePickerInline/utils.ts +367 -0
  84. package/components/DatePickerInput/DatePickerInput.tsx +139 -0
  85. package/components/DatePickerInput/DatePickerInputModal.tsx +48 -0
  86. package/components/DatePickerInput/DatePickerInputWithoutModal.tsx +77 -0
  87. package/components/DatePickerInput/DateRangeInput.tsx +88 -0
  88. package/components/DatePickerInput/index.tsx +14 -0
  89. package/components/DatePickerInput/inputUtils.ts +138 -0
  90. package/components/DatePickerInput/types.ts +28 -0
  91. package/components/DatePickerInput/utils.ts +16 -0
  92. package/components/DatePickerModal/AnimatedCrossView.tsx +94 -0
  93. package/components/DatePickerModal/CalendarEdit.tsx +139 -0
  94. package/components/DatePickerModal/DatePickerModal.tsx +85 -0
  95. package/components/DatePickerModal/DatePickerModalContent.tsx +155 -0
  96. package/components/DatePickerModal/DatePickerModalContentHeader.tsx +213 -0
  97. package/components/DatePickerModal/DatePickerModalHeader.tsx +74 -0
  98. package/components/DatePickerModal/DatePickerModalHeaderBackground.tsx +13 -0
  99. package/components/DatePickerModal/index.tsx +20 -0
  100. package/components/DatePickerModal/types.ts +92 -0
  101. package/components/DatePickerModal/utils.ts +121 -0
  102. package/components/DateTimePicker/DateTimePicker.tsx +172 -0
  103. package/components/DateTimePicker/index.tsx +14 -0
  104. package/components/DateTimePicker/utils.ts +13 -0
  105. package/components/Dialog/Dialog.tsx +66 -0
  106. package/components/Dialog/DialogActions.tsx +89 -0
  107. package/components/Dialog/DialogContent.tsx +37 -0
  108. package/components/Dialog/DialogIcon.tsx +69 -0
  109. package/components/Dialog/DialogTitle.tsx +68 -0
  110. package/components/Dialog/index.tsx +37 -0
  111. package/components/Dialog/utils.ts +80 -0
  112. package/components/Drawer/Collapsible/DrawerCollapsible.tsx +22 -0
  113. package/components/Drawer/Collapsible/DrawerCollapsibleItem.tsx +38 -0
  114. package/components/Drawer/Collapsible/DrawerCollapsibleItemContent.tsx +16 -0
  115. package/components/Drawer/Collapsible/DrawerCollapsibleItemHeader.tsx +48 -0
  116. package/components/Drawer/Collapsible/index.tsx +23 -0
  117. package/components/Drawer/Collapsible/utils.ts +49 -0
  118. package/components/Drawer/Drawer.tsx +43 -0
  119. package/components/Drawer/DrawerContent.tsx +35 -0
  120. package/components/Drawer/DrawerFooter.tsx +27 -0
  121. package/components/Drawer/DrawerHeader.tsx +27 -0
  122. package/components/Drawer/DrawerItem.tsx +206 -0
  123. package/components/Drawer/DrawerItemGroup.tsx +82 -0
  124. package/components/Drawer/index.tsx +47 -0
  125. package/components/Drawer/types.ts +3 -0
  126. package/components/Drawer/utils.ts +8 -0
  127. package/components/ElementGroup/ElementGroup.tsx +139 -0
  128. package/components/ElementGroup/index.tsx +11 -0
  129. package/components/ElementGroup/utils.ts +25 -0
  130. package/components/FAB/FAB.tsx +176 -0
  131. package/components/FAB/index.tsx +12 -0
  132. package/components/FAB/types.ts +1 -0
  133. package/components/FAB/utils.ts +221 -0
  134. package/components/FilePicker/FilePicker.tsx +133 -0
  135. package/components/FilePicker/index.tsx +11 -0
  136. package/components/FilePicker/utils.ts +13 -0
  137. package/components/HelperText/HelperText.tsx +139 -0
  138. package/components/HelperText/index.tsx +11 -0
  139. package/components/HelperText/utils.ts +29 -0
  140. package/components/HorizontalDivider/HorizontalDivider.tsx +101 -0
  141. package/components/HorizontalDivider/index.tsx +13 -0
  142. package/components/Icon/CrossFadeIcon.tsx +116 -0
  143. package/components/Icon/Icon.tsx +41 -0
  144. package/components/Icon/iconFactory.tsx +23 -0
  145. package/components/Icon/index.tsx +11 -0
  146. package/components/Icon/types.ts +35 -0
  147. package/components/IconButton/IconButton.tsx +218 -0
  148. package/components/IconButton/index.tsx +11 -0
  149. package/components/IconButton/types.ts +1 -0
  150. package/components/IconButton/utils.ts +325 -0
  151. package/components/If/index.tsx +13 -0
  152. package/components/InputAddon/InputAddon.tsx +27 -0
  153. package/components/InputAddon/index.tsx +11 -0
  154. package/components/InputAddon/utils.ts +33 -0
  155. package/components/Link/Link.tsx +48 -0
  156. package/components/Link/index.tsx +11 -0
  157. package/components/Link/utils.ts +37 -0
  158. package/components/ListItem/ListItem.tsx +136 -0
  159. package/components/ListItem/ListItemDescription.tsx +25 -0
  160. package/components/ListItem/ListItemTitle.tsx +25 -0
  161. package/components/ListItem/index.tsx +18 -0
  162. package/components/ListItem/utils.ts +113 -0
  163. package/components/Menu/Menu.tsx +69 -0
  164. package/components/Menu/MenuDivider.tsx +13 -0
  165. package/components/Menu/MenuItem.tsx +128 -0
  166. package/components/Menu/index.tsx +19 -0
  167. package/components/Menu/utils.ts +92 -0
  168. package/components/Modal/Modal.tsx +261 -0
  169. package/components/Modal/index.tsx +11 -0
  170. package/components/Modal/utils.ts +45 -0
  171. package/components/NavigationRail/NavigationRail.tsx +32 -0
  172. package/components/NavigationRail/NavigationRailContent.tsx +25 -0
  173. package/components/NavigationRail/NavigationRailFooter.tsx +18 -0
  174. package/components/NavigationRail/NavigationRailHeader.tsx +18 -0
  175. package/components/NavigationRail/NavigationRailItem.tsx +226 -0
  176. package/components/NavigationRail/index.tsx +35 -0
  177. package/components/NavigationRail/utils.ts +170 -0
  178. package/components/NavigationStack/NavigationStack.tsx +85 -0
  179. package/components/NavigationStack/NavigationStackItem.tsx +60 -0
  180. package/components/NavigationStack/index.tsx +20 -0
  181. package/components/NavigationStack/utils.tsx +16 -0
  182. package/components/Popover/Popover.native.tsx +185 -0
  183. package/components/Popover/Popover.tsx +198 -0
  184. package/components/Popover/common.ts +459 -0
  185. package/components/Popover/index.ts +2 -0
  186. package/components/Portal/Portal.tsx +13 -0
  187. package/components/Portal/index.tsx +12 -0
  188. package/components/RadioButton/RadioButton.tsx +138 -0
  189. package/components/RadioButton/RadioButtonAndroid.tsx +188 -0
  190. package/components/RadioButton/RadioButtonGroup.tsx +98 -0
  191. package/components/RadioButton/RadioButtonIOS.tsx +106 -0
  192. package/components/RadioButton/RadioButtonItem.tsx +232 -0
  193. package/components/RadioButton/index.ts +27 -0
  194. package/components/RadioButton/utils.ts +164 -0
  195. package/components/Rating/Rating.tsx +149 -0
  196. package/components/Rating/RatingItem.tsx +125 -0
  197. package/components/Rating/index.tsx +13 -0
  198. package/components/Rating/utils.ts +38 -0
  199. package/components/Select/Select.tsx +1038 -0
  200. package/components/Select/index.ts +14 -0
  201. package/components/Select/types.ts +115 -0
  202. package/components/StateLayer/StateLayer.tsx +12 -0
  203. package/components/StateLayer/index.tsx +11 -0
  204. package/components/StateLayer/utils.ts +17 -0
  205. package/components/Surface/BackgroundContextWrapper.tsx +27 -0
  206. package/components/Surface/Surface.android.tsx +62 -0
  207. package/components/Surface/Surface.ios.tsx +123 -0
  208. package/components/Surface/Surface.tsx +48 -0
  209. package/components/Surface/index.tsx +12 -0
  210. package/components/Surface/utils.ts +106 -0
  211. package/components/Switch/Switch.ios.tsx +67 -0
  212. package/components/Switch/Switch.tsx +278 -0
  213. package/components/Switch/index.tsx +11 -0
  214. package/components/Switch/utils.ts +283 -0
  215. package/components/Tabs/TabItem.tsx +150 -0
  216. package/components/Tabs/TabLabel.tsx +84 -0
  217. package/components/Tabs/Tabs.tsx +398 -0
  218. package/components/Tabs/index.tsx +21 -0
  219. package/components/Tabs/utils.ts +126 -0
  220. package/components/Text/Text.tsx +23 -0
  221. package/components/Text/index.tsx +2 -0
  222. package/components/Text/textFactory.tsx +33 -0
  223. package/components/TextInput/InputLabel.tsx +181 -0
  224. package/components/TextInput/TextInput.tsx +693 -0
  225. package/components/TextInput/index.tsx +16 -0
  226. package/components/TextInput/types.ts +96 -0
  227. package/components/TextInput/utils.ts +544 -0
  228. package/components/TextInputWithMask/TextInputMask.tsx +57 -0
  229. package/components/TextInputWithMask/index.tsx +11 -0
  230. package/components/TextInputWithMask/utils.ts +56 -0
  231. package/components/TimePicker/AmPmSwitcher.tsx +99 -0
  232. package/components/TimePicker/AnalogClock.tsx +165 -0
  233. package/components/TimePicker/AnalogClockHours.tsx +163 -0
  234. package/components/TimePicker/AnalogClockMinutes.tsx +68 -0
  235. package/components/TimePicker/AnimatedClockSwitcher.tsx +72 -0
  236. package/components/TimePicker/DisplayModeContext.tsx +6 -0
  237. package/components/TimePicker/TimeInput.tsx +112 -0
  238. package/components/TimePicker/TimeInputs.tsx +148 -0
  239. package/components/TimePicker/TimePicker.tsx +130 -0
  240. package/components/TimePicker/index.tsx +19 -0
  241. package/components/TimePicker/timeUtils.ts +159 -0
  242. package/components/TimePicker/utils.ts +285 -0
  243. package/components/TimePickerField/TimePickerField.tsx +152 -0
  244. package/components/TimePickerField/index.tsx +14 -0
  245. package/components/TimePickerField/sanitizeTime.ts +85 -0
  246. package/components/TimePickerField/utils.ts +95 -0
  247. package/components/TimePickerModal/TimePickerModal.tsx +115 -0
  248. package/components/TimePickerModal/index.tsx +14 -0
  249. package/components/TimePickerModal/utils.ts +48 -0
  250. package/components/Tooltip/Tooltip.tsx +137 -0
  251. package/components/Tooltip/TooltipContent.tsx +12 -0
  252. package/components/Tooltip/TooltipTrigger.tsx +94 -0
  253. package/components/Tooltip/index.tsx +20 -0
  254. package/components/Tooltip/utils.ts +21 -0
  255. package/components/TouchableRipple/TouchableRipple.native.tsx +105 -0
  256. package/components/TouchableRipple/TouchableRipple.tsx +286 -0
  257. package/components/TouchableRipple/index.tsx +14 -0
  258. package/components/TouchableRipple/utils.ts +15 -0
  259. package/components/VerticalDivider/VerticalDivider.tsx +99 -0
  260. package/components/VerticalDivider/index.tsx +13 -0
  261. package/context-bridge/index.tsx +87 -0
  262. package/core/componentsRegistry.ts +164 -0
  263. package/core/index.tsx +2 -0
  264. package/fast-context/index.tsx +190 -0
  265. package/hocs/index.tsx +5 -0
  266. package/hocs/typedMemo.tsx +5 -0
  267. package/hocs/withKeyboardAccessibility.tsx +231 -0
  268. package/hocs/withPortal.tsx +16 -0
  269. package/hooks/createPsuedoHook.tsx +50 -0
  270. package/hooks/index.tsx +29 -0
  271. package/hooks/useActionState.native.tsx +22 -0
  272. package/hooks/useActionState.tsx +34 -0
  273. package/hooks/useActive.tsx +5 -0
  274. package/hooks/useBreakpoints.tsx +7 -0
  275. package/hooks/useColorMode.tsx +17 -0
  276. package/hooks/useContrastColor.ts +15 -0
  277. package/hooks/useControlledValue.tsx +68 -0
  278. package/hooks/useFilePicker.tsx +48 -0
  279. package/hooks/useFocus.tsx +5 -0
  280. package/hooks/useHandleNumberFormat.tsx +106 -0
  281. package/hooks/useHover.tsx +5 -0
  282. package/hooks/useKeyboardDismissable.ts +66 -0
  283. package/hooks/useLatest.tsx +9 -0
  284. package/hooks/useMediaQuery.tsx +64 -0
  285. package/hooks/useMergedRefs.ts +14 -0
  286. package/hooks/usePrevious.ts +13 -0
  287. package/hooks/useQueryFilter.tsx +35 -0
  288. package/hooks/useSearchable.tsx +74 -0
  289. package/hooks/useSubcomponents.tsx +59 -0
  290. package/hooks/useTheme.ts +3 -0
  291. package/hooks/useToggle.tsx +24 -0
  292. package/package.json +114 -0
  293. package/shortcuts-manager/EventsManager.tsx +121 -0
  294. package/shortcuts-manager/ShortcutsManager/ShortcutsManager.native.tsx +9 -0
  295. package/shortcuts-manager/ShortcutsManager/ShortcutsManager.tsx +58 -0
  296. package/shortcuts-manager/ShortcutsManager/index.tsx +2 -0
  297. package/shortcuts-manager/ShortcutsManager/utils.tsx +30 -0
  298. package/shortcuts-manager/index.tsx +6 -0
  299. package/shortcuts-manager/types.ts +24 -0
  300. package/shortcuts-manager/useIsKeyPressed/index.tsx +1 -0
  301. package/shortcuts-manager/useIsKeyPressed/useIsKeyPress.tsx +9 -0
  302. package/shortcuts-manager/useSetScopes/index.tsx +1 -0
  303. package/shortcuts-manager/useSetScopes/useSetScopes.native.tsx +9 -0
  304. package/shortcuts-manager/useSetScopes/useSetScopes.tsx +25 -0
  305. package/shortcuts-manager/useShortcut/index.tsx +2 -0
  306. package/shortcuts-manager/useShortcut/types.ts +3 -0
  307. package/shortcuts-manager/useShortcut/useShortcut.native.tsx +9 -0
  308. package/shortcuts-manager/useShortcut/useShortcut.tsx +61 -0
  309. package/shortcuts-manager/utils.ts +105 -0
  310. package/styles/index.ts +4 -0
  311. package/styles/overlay.ts +69 -0
  312. package/styles/shadow.ts +21 -0
  313. package/styles/themes/DarkTheme.tsx +98 -0
  314. package/styles/themes/LightTheme.tsx +212 -0
  315. package/styles/themes/tokens.ts +248 -0
  316. package/styles/utils.ts +11 -0
  317. package/types/index.ts +129 -0
  318. package/types/theme.ts +159 -0
  319. package/utils/DocumentPicker/documentPicker.native.ts +10 -0
  320. package/utils/DocumentPicker/documentPicker.ts +76 -0
  321. package/utils/DocumentPicker/index.ts +2 -0
  322. package/utils/DocumentPicker/types.ts +28 -0
  323. package/utils/addEventListener.tsx +51 -0
  324. package/utils/backgroundContext.ts +9 -0
  325. package/utils/color.ts +22 -0
  326. package/utils/compare/index.ts +54 -0
  327. package/utils/composeEventHandlers.ts +9 -0
  328. package/utils/createNumberMask/createNumberMask.ts +98 -0
  329. package/utils/createNumberMask/index.ts +5 -0
  330. package/utils/createSyntheticEvent.ts +31 -0
  331. package/utils/date-fns.ts +7 -0
  332. package/utils/dateTimePicker.ts +5 -0
  333. package/utils/escapeRegex.ts +9 -0
  334. package/utils/extractTextStyles.ts +52 -0
  335. package/utils/formatNumberWithMask/formatNumberWithMask.ts +26 -0
  336. package/utils/formatNumberWithMask/formatWithMask.ts +119 -0
  337. package/utils/formatNumberWithMask/index.ts +6 -0
  338. package/utils/getCursorStyle/getCursorStyle.native.ts +1 -0
  339. package/utils/getCursorStyle/getCursorStyle.ts +1 -0
  340. package/utils/getCursorStyle/index.ts +1 -0
  341. package/utils/getOS/getOS.native.ts +7 -0
  342. package/utils/getOS/getOS.ts +24 -0
  343. package/utils/getOS/index.ts +1 -0
  344. package/utils/getyearRange.ts +5 -0
  345. package/utils/index.ts +20 -0
  346. package/utils/lodash.ts +50 -0
  347. package/utils/mergeRefs.ts +13 -0
  348. package/utils/normalizeBorderRadiuses.ts +24 -0
  349. package/utils/normalizeSpacings.ts +110 -0
  350. package/utils/normalizeToNumberString/index.ts +4 -0
  351. package/utils/normalizeToNumberString/normalizeToNumberString.ts +48 -0
  352. package/utils/repository.ts +103 -0
  353. package/utils/resolveColorMode.ts +9 -0
  354. package/utils/resolveStateVariant.ts +26 -0
  355. package/utils/tokenStylesParser.ts +7 -0
@@ -0,0 +1,198 @@
1
+ import { Fragment, memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
2
+ import { Pressable, View } from 'react-native';
3
+ import { ScopedTheme, StyleSheet, UnistylesRuntime } from 'react-native-unistyles';
4
+
5
+ import { Portal } from '../Portal';
6
+ import {
7
+ DEFAULT_ARROW_SIZE,
8
+ popoverDefaultStyles,
9
+ type PopoverProps,
10
+ useArrowStyles,
11
+ usePopover,
12
+ } from './common';
13
+
14
+ const Popover = ({
15
+ triggerRef,
16
+ children,
17
+ isOpen,
18
+ onClose,
19
+ position = 'bottom',
20
+ align = 'center',
21
+ style,
22
+ showArrow = false,
23
+ arrowSize = DEFAULT_ARROW_SIZE,
24
+ inverted = false,
25
+ // @ts-ignore
26
+ dataSet,
27
+ withBackdropDismiss = false,
28
+ offset = 8,
29
+ backdropStyles,
30
+ triggerDimensions,
31
+ ...rest
32
+ }: PopoverProps) => {
33
+ const {
34
+ popoverLayoutRef,
35
+ targetLayoutRef,
36
+ actualPositionRef,
37
+ calculatedPosition,
38
+ calculateAndSetPosition,
39
+ handlePopoverLayout,
40
+ } = usePopover({
41
+ isOpen,
42
+ position,
43
+ align,
44
+ showArrow,
45
+ arrowSize,
46
+ offset,
47
+ });
48
+
49
+ const popoverRef = useRef<View>(null);
50
+
51
+ const measureTarget = useCallback(() => {
52
+ if (triggerRef.current) {
53
+ triggerRef.current.measureInWindow(
54
+ (x: number, y: number, width: number, height: number) => {
55
+ if (width !== 0 || height !== 0) {
56
+ const newLayout = { x, y, width, height };
57
+ const changed =
58
+ !targetLayoutRef.current ||
59
+ targetLayoutRef.current.x !== newLayout.x ||
60
+ targetLayoutRef.current.y !== newLayout.y ||
61
+ targetLayoutRef.current.width !== newLayout.width ||
62
+ targetLayoutRef.current.height !== newLayout.height;
63
+
64
+ if (changed) {
65
+ targetLayoutRef.current = newLayout;
66
+ calculateAndSetPosition();
67
+ }
68
+ } else {
69
+ targetLayoutRef.current = null;
70
+ calculateAndSetPosition();
71
+ }
72
+ },
73
+ );
74
+ } else {
75
+ targetLayoutRef.current = null;
76
+ calculateAndSetPosition();
77
+ }
78
+ }, [triggerRef, calculateAndSetPosition, targetLayoutRef]);
79
+
80
+ useLayoutEffect(() => {
81
+ if (isOpen) {
82
+ const timeoutId = setTimeout(measureTarget, 0);
83
+ return () => clearTimeout(timeoutId);
84
+ }
85
+ return;
86
+ }, [isOpen, measureTarget, triggerDimensions]);
87
+
88
+ useLayoutEffect(() => {
89
+ if (!isOpen) return;
90
+ const handleResize = () => {
91
+ if (triggerRef.current && isOpen) {
92
+ window.requestAnimationFrame(measureTarget);
93
+ }
94
+ };
95
+ window.addEventListener('resize', handleResize);
96
+ window.addEventListener('scroll', handleResize, true);
97
+ return () => {
98
+ window.removeEventListener('resize', handleResize);
99
+ window.removeEventListener('scroll', handleResize, true);
100
+ };
101
+ }, [isOpen, measureTarget, triggerRef]);
102
+
103
+ useEffect(() => {
104
+ if (!isOpen || !onClose || withBackdropDismiss) return;
105
+ const handleClickOutside = (event: MouseEvent) => {
106
+ const popoverElement = popoverRef.current as any as HTMLElement;
107
+ const targetElement = triggerRef.current as any as HTMLElement;
108
+ if (
109
+ popoverElement &&
110
+ !popoverElement.contains(event.target as Node) &&
111
+ targetElement &&
112
+ !targetElement.contains(event.target as Node)
113
+ ) {
114
+ onClose();
115
+ }
116
+ };
117
+ document.addEventListener('mousedown', handleClickOutside, { capture: true });
118
+ return () => {
119
+ document.removeEventListener('mousedown', handleClickOutside, { capture: true });
120
+ };
121
+ }, [isOpen, onClose, popoverRef, triggerRef, withBackdropDismiss]);
122
+
123
+ const arrowStyles = useArrowStyles({
124
+ showArrow,
125
+ arrowSize,
126
+ style,
127
+ calculatedPosition,
128
+ targetLayoutRef,
129
+ popoverLayoutRef,
130
+ actualPositionRef,
131
+ });
132
+
133
+ const popoverStyle = useMemo(() => {
134
+ if (!calculatedPosition) return popoverDefaultStyles;
135
+
136
+ const scrollX = window.scrollX ?? window.pageXOffset ?? 0;
137
+ const scrollY = window.scrollY ?? window.pageYOffset ?? 0;
138
+
139
+ return {
140
+ ...calculatedPosition,
141
+ left: (calculatedPosition.left as number) + scrollX,
142
+ top: (calculatedPosition.top as number) + scrollY,
143
+ };
144
+ }, [calculatedPosition]);
145
+
146
+ const Wrapper = inverted ? ScopedTheme : Fragment;
147
+ const WrapperProps = inverted
148
+ ? { name: UnistylesRuntime.themeName === 'dark' ? 'light' : 'dark', reset: false }
149
+ : {};
150
+
151
+ if (!isOpen && popoverStyle.opacity === 0) {
152
+ return null;
153
+ }
154
+
155
+ return (
156
+ <Portal>
157
+ <Wrapper {...(WrapperProps as any)}>
158
+ {withBackdropDismiss && (
159
+ <Pressable style={[styles.backdrop, backdropStyles]} onPress={onClose} />
160
+ )}
161
+ <View
162
+ onLayout={handlePopoverLayout}
163
+ style={[styles.popoverContainer, style, popoverStyle]}
164
+ {...{ dataSet }}
165
+ {...rest}
166
+ ref={popoverRef}>
167
+ {children}
168
+ {showArrow && popoverStyle.opacity === 1 && <View style={arrowStyles} />}
169
+ </View>
170
+ </Wrapper>
171
+ </Portal>
172
+ );
173
+ };
174
+
175
+ const styles = StyleSheet.create(theme => ({
176
+ popoverContainer: {
177
+ ...popoverDefaultStyles,
178
+ backgroundColor: theme.colors.surface,
179
+ borderRadius: 4,
180
+ shadowColor: 'rgba(0, 0, 0, 1)',
181
+ shadowOffset: { width: 0, height: 2 },
182
+ shadowOpacity: theme.dark ? 0.7 : 0.3,
183
+ shadowRadius: 10,
184
+ zIndex: 100,
185
+ },
186
+ backdrop: {
187
+ position: 'absolute',
188
+ top: 0,
189
+ left: 0,
190
+ right: 0,
191
+ bottom: 0,
192
+ _web: {
193
+ cursor: 'default',
194
+ },
195
+ },
196
+ }));
197
+
198
+ export default memo(Popover);
@@ -0,0 +1,459 @@
1
+ import type { ReactNode, RefObject } from 'react';
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import type { LayoutRectangle, StyleProp, View, ViewStyle } from 'react-native';
4
+ import { Dimensions, StyleSheet } from 'react-native';
5
+
6
+ export type Position = 'top' | 'left' | 'right' | 'bottom';
7
+ export type Align = 'start' | 'center' | 'end';
8
+
9
+ export const DEFAULT_ARROW_SIZE = 10;
10
+
11
+ export const popoverDefaultStyles = {
12
+ position: 'absolute' as const,
13
+ top: -9999,
14
+ left: -9999,
15
+ opacity: 0,
16
+ };
17
+
18
+ export type PopoverProps = {
19
+ inverted?: boolean;
20
+ /** Reference to the element the popover should anchor to */
21
+ triggerRef: RefObject<View | any>; // Allow different ref types
22
+ /** Content to display inside the popover */
23
+ children: ReactNode;
24
+ /** Whether the popover is visible */
25
+ isOpen: boolean;
26
+ /** Callback when the popover requests to be closed */
27
+ onClose?: () => void;
28
+ /** Preferred position of the popover relative to the target */
29
+ position?: Position;
30
+ /** Alignment of the popover relative to the target */
31
+ align?: Align;
32
+ /** Optional style for the popover container */
33
+ style?: StyleProp<ViewStyle>;
34
+ /** Show arrow pointing to the target */
35
+ showArrow?: boolean;
36
+ /** Size of the arrow */
37
+ arrowSize?: number;
38
+ withBackdropDismiss?: boolean;
39
+ offset?: number;
40
+ backdropStyles?: StyleProp<ViewStyle>;
41
+ /** Optional trigger dimensions to trigger re-measurement when changed */
42
+ triggerDimensions?: { width: number; height: number } | null;
43
+ };
44
+
45
+ // --- Positioning Helper Functions ---
46
+
47
+ export const getInitialPosition = (
48
+ position: PopoverProps['position'],
49
+ align: PopoverProps['align'],
50
+ targetLayout: LayoutRectangle,
51
+ popoverLayout: LayoutRectangle,
52
+ effectiveArrowSize: number,
53
+ ): { top: number; left: number } => {
54
+ const { x: targetX, y: targetY, width: targetWidth, height: targetHeight } = targetLayout;
55
+ const { width: popoverWidth, height: popoverHeight } = popoverLayout;
56
+ let top = 0;
57
+ let left = 0;
58
+
59
+ switch (position) {
60
+ case 'top':
61
+ top = targetY - popoverHeight - effectiveArrowSize;
62
+ break;
63
+ case 'bottom':
64
+ top = targetY + targetHeight + effectiveArrowSize;
65
+ break;
66
+ case 'left':
67
+ left = targetX - popoverWidth - effectiveArrowSize;
68
+ break;
69
+ case 'right':
70
+ left = targetX + targetWidth + effectiveArrowSize;
71
+ break;
72
+ }
73
+
74
+ switch (position) {
75
+ case 'top':
76
+ case 'bottom':
77
+ switch (align) {
78
+ case 'start':
79
+ left = targetX;
80
+ break;
81
+ case 'center':
82
+ left = targetX + targetWidth / 2 - popoverWidth / 2;
83
+ break;
84
+ case 'end':
85
+ left = targetX + targetWidth - popoverWidth;
86
+ break;
87
+ }
88
+ break;
89
+ case 'left':
90
+ case 'right':
91
+ switch (align) {
92
+ case 'start':
93
+ top = targetY;
94
+ break;
95
+ case 'center':
96
+ top = targetY + targetHeight / 2 - popoverHeight / 2;
97
+ break;
98
+ case 'end':
99
+ top = targetY + targetHeight - popoverHeight;
100
+ break;
101
+ }
102
+ break;
103
+ }
104
+
105
+ return { top, left };
106
+ };
107
+
108
+ export const adjustPositionForBoundaries = (
109
+ currentPosition: PopoverProps['position'],
110
+ top: number,
111
+ left: number,
112
+ targetLayout: LayoutRectangle,
113
+ popoverLayout: LayoutRectangle,
114
+ screenHeight: number,
115
+ screenWidth: number,
116
+ effectiveArrowSize: number,
117
+ ): { finalTop: number; finalLeft: number; finalPosition: PopoverProps['position'] } => {
118
+ const { x: targetX, y: targetY, width: targetWidth, height: targetHeight } = targetLayout;
119
+ const { width: popoverWidth, height: popoverHeight } = popoverLayout;
120
+ let adjustedPosition = currentPosition;
121
+ let adjustedTop = top;
122
+ let adjustedLeft = left;
123
+
124
+ // Check vertical adjustments
125
+ if (
126
+ adjustedPosition === 'top' &&
127
+ adjustedTop < 0 &&
128
+ targetY + targetHeight + popoverHeight + effectiveArrowSize <= screenHeight
129
+ ) {
130
+ adjustedPosition = 'bottom';
131
+ adjustedTop = targetY + targetHeight + effectiveArrowSize;
132
+ } else if (
133
+ adjustedPosition === 'bottom' &&
134
+ adjustedTop + popoverHeight > screenHeight &&
135
+ targetY - popoverHeight - effectiveArrowSize >= 0
136
+ ) {
137
+ adjustedPosition = 'top';
138
+ adjustedTop = targetY - popoverHeight - effectiveArrowSize;
139
+ }
140
+
141
+ // Check horizontal adjustments
142
+ if (
143
+ adjustedPosition === 'left' &&
144
+ adjustedLeft < 0 &&
145
+ targetX + targetWidth + popoverWidth + effectiveArrowSize <= screenWidth
146
+ ) {
147
+ adjustedPosition = 'right';
148
+ adjustedLeft = targetX + targetWidth + effectiveArrowSize;
149
+ } else if (
150
+ adjustedPosition === 'right' &&
151
+ adjustedLeft + popoverWidth > screenWidth &&
152
+ targetX - popoverWidth - effectiveArrowSize >= 0
153
+ ) {
154
+ adjustedPosition = 'left';
155
+ adjustedLeft = targetX - popoverWidth - effectiveArrowSize;
156
+ }
157
+
158
+ // Final clamping to screen bounds
159
+ const finalTop = Math.max(0, Math.min(adjustedTop, screenHeight - popoverHeight));
160
+ const finalLeft = Math.max(0, Math.min(adjustedLeft, screenWidth - popoverWidth));
161
+
162
+ return { finalTop, finalLeft, finalPosition: adjustedPosition };
163
+ };
164
+
165
+ // --- Arrow Style Hook ---
166
+
167
+ interface UseArrowStylesProps {
168
+ showArrow?: boolean;
169
+ arrowSize: number;
170
+ style?: StyleProp<ViewStyle>;
171
+ calculatedPosition: ViewStyle | null;
172
+ targetLayoutRef: RefObject<LayoutRectangle | null>;
173
+ popoverLayoutRef: RefObject<LayoutRectangle | null>;
174
+ actualPositionRef: RefObject<Position | undefined>; // Use Position type
175
+ }
176
+
177
+ // Define a base style for the popover container to extract default background
178
+ const basePopoverStyle = StyleSheet.create({
179
+ container: {
180
+ backgroundColor: 'white',
181
+ zIndex: 100,
182
+ },
183
+ });
184
+
185
+ export const useArrowStyles = ({
186
+ showArrow,
187
+ arrowSize,
188
+ style,
189
+ calculatedPosition,
190
+ targetLayoutRef,
191
+ popoverLayoutRef,
192
+ actualPositionRef,
193
+ }: UseArrowStylesProps): ViewStyle => {
194
+ return useMemo(() => {
195
+ if (
196
+ !showArrow ||
197
+ !targetLayoutRef.current ||
198
+ !popoverLayoutRef.current ||
199
+ !calculatedPosition ||
200
+ calculatedPosition.opacity !== 1 ||
201
+ !actualPositionRef.current
202
+ )
203
+ return {};
204
+
205
+ const popoverX = calculatedPosition.left as number;
206
+ const popoverY = calculatedPosition.top as number;
207
+ const { width: popoverWidth, height: popoverHeight } = popoverLayoutRef.current;
208
+ const {
209
+ x: targetX,
210
+ y: targetY,
211
+ width: targetWidth,
212
+ height: targetHeight,
213
+ } = targetLayoutRef.current;
214
+
215
+ const arrowHalfSize = arrowSize / 2;
216
+ const popoverStyleFlat = StyleSheet.flatten(style || {});
217
+ const containerStyleFlat = StyleSheet.flatten(basePopoverStyle.container);
218
+
219
+ const backgroundColor =
220
+ popoverStyleFlat.backgroundColor || containerStyleFlat.backgroundColor;
221
+ const zIndex = popoverStyleFlat.zIndex ?? containerStyleFlat.zIndex;
222
+
223
+ const baseArrowStyle: ViewStyle = {
224
+ position: 'absolute',
225
+ width: 0,
226
+ height: 0,
227
+ borderStyle: 'solid',
228
+ backgroundColor: 'transparent',
229
+ zIndex: typeof zIndex === 'number' ? zIndex + 1 : 101, // Ensure arrow is above popover
230
+ };
231
+
232
+ let arrowTop = 0;
233
+ let arrowLeft = 0;
234
+
235
+ switch (actualPositionRef.current) {
236
+ case 'bottom':
237
+ arrowTop = -arrowSize; // Relative to popover container
238
+ arrowLeft = Math.max(
239
+ arrowHalfSize,
240
+ Math.min(
241
+ targetX + targetWidth / 2 - popoverX - arrowHalfSize,
242
+ popoverWidth - arrowHalfSize * 2,
243
+ ),
244
+ );
245
+ return {
246
+ ...baseArrowStyle,
247
+ top: arrowTop,
248
+ left: arrowLeft,
249
+ borderTopWidth: 0,
250
+ borderRightWidth: arrowHalfSize,
251
+ borderBottomWidth: arrowSize,
252
+ borderLeftWidth: arrowHalfSize,
253
+ borderRightColor: 'transparent',
254
+ borderBottomColor: backgroundColor,
255
+ borderLeftColor: 'transparent',
256
+ };
257
+ case 'top':
258
+ arrowTop = popoverHeight; // Relative to popover container
259
+ arrowLeft = Math.max(
260
+ arrowHalfSize,
261
+ Math.min(
262
+ targetX + targetWidth / 2 - popoverX - arrowHalfSize,
263
+ popoverWidth - arrowHalfSize * 2,
264
+ ),
265
+ );
266
+ return {
267
+ ...baseArrowStyle,
268
+ top: arrowTop,
269
+ left: arrowLeft,
270
+ borderTopWidth: arrowSize,
271
+ borderRightWidth: arrowHalfSize,
272
+ borderBottomWidth: 0,
273
+ borderLeftWidth: arrowHalfSize,
274
+ borderTopColor: backgroundColor,
275
+ borderRightColor: 'transparent',
276
+ borderLeftColor: 'transparent',
277
+ };
278
+ case 'right':
279
+ arrowLeft = -arrowSize; // Relative to popover container
280
+ arrowTop = Math.max(
281
+ arrowHalfSize,
282
+ Math.min(
283
+ targetY + targetHeight / 2 - popoverY - arrowHalfSize,
284
+ popoverHeight - arrowHalfSize * 2,
285
+ ),
286
+ );
287
+ return {
288
+ ...baseArrowStyle,
289
+ top: arrowTop,
290
+ left: arrowLeft,
291
+ borderTopWidth: arrowHalfSize,
292
+ borderRightWidth: arrowSize,
293
+ borderBottomWidth: arrowHalfSize,
294
+ borderLeftWidth: 0,
295
+ borderTopColor: 'transparent',
296
+ borderRightColor: backgroundColor,
297
+ borderBottomColor: 'transparent',
298
+ };
299
+ case 'left':
300
+ arrowLeft = popoverWidth; // Relative to popover container
301
+ arrowTop = Math.max(
302
+ arrowHalfSize,
303
+ Math.min(
304
+ targetY + targetHeight / 2 - popoverY - arrowHalfSize,
305
+ popoverHeight - arrowHalfSize * 2,
306
+ ),
307
+ );
308
+ return {
309
+ ...baseArrowStyle,
310
+ top: arrowTop,
311
+ left: arrowLeft,
312
+ borderTopWidth: arrowHalfSize,
313
+ borderRightWidth: 0,
314
+ borderBottomWidth: arrowHalfSize,
315
+ borderLeftWidth: arrowSize,
316
+ borderTopColor: 'transparent',
317
+ borderBottomColor: 'transparent',
318
+ borderLeftColor: backgroundColor,
319
+ };
320
+ default:
321
+ return {};
322
+ }
323
+ // Use refs directly in dependency array for useMemo
324
+ // React checks ref.current internally when deciding memoization
325
+ }, [
326
+ showArrow,
327
+ arrowSize,
328
+ style, // Include style for potential background/zIndex changes
329
+ calculatedPosition,
330
+ targetLayoutRef, // Dependency on the ref objects
331
+ popoverLayoutRef,
332
+ actualPositionRef,
333
+ ]);
334
+ };
335
+
336
+ // --- Core Popover Logic Hook ---
337
+
338
+ interface UsePopoverProps {
339
+ isOpen: boolean;
340
+ position: Position | undefined;
341
+ align: Align | undefined;
342
+ showArrow: boolean | undefined;
343
+ arrowSize: number;
344
+ offset?: number;
345
+ }
346
+
347
+ export const usePopover = ({
348
+ isOpen,
349
+ position = 'bottom',
350
+ align = 'center',
351
+ showArrow = true,
352
+ arrowSize = DEFAULT_ARROW_SIZE,
353
+ offset = 0,
354
+ }: UsePopoverProps) => {
355
+ const popoverLayoutRef = useRef<LayoutRectangle | null>(null);
356
+ const targetLayoutRef = useRef<LayoutRectangle | null>(null);
357
+ const actualPositionRef = useRef<Position | undefined>(position);
358
+ const [calculatedPosition, setCalculatedPosition] = useState<ViewStyle>(popoverDefaultStyles);
359
+
360
+ const calculateAndSetPosition = useCallback(() => {
361
+ if (!targetLayoutRef.current || !popoverLayoutRef.current) {
362
+ setCalculatedPosition(popoverDefaultStyles); // Hide if layouts are not ready
363
+ return;
364
+ }
365
+
366
+ const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
367
+ const effectiveArrowSize = showArrow ? arrowSize : 0;
368
+
369
+ let { top: initialTop, left: initialLeft } = getInitialPosition(
370
+ position,
371
+ align,
372
+ targetLayoutRef.current,
373
+ popoverLayoutRef.current,
374
+ effectiveArrowSize,
375
+ );
376
+
377
+ // Apply offset based on the initial intended position
378
+ switch (position) {
379
+ case 'top':
380
+ initialTop -= offset;
381
+ break;
382
+ case 'bottom':
383
+ initialTop += offset;
384
+ break;
385
+ case 'left':
386
+ initialLeft -= offset;
387
+ break;
388
+ case 'right':
389
+ initialLeft += offset;
390
+ break;
391
+ }
392
+
393
+ const { finalTop, finalLeft, finalPosition } = adjustPositionForBoundaries(
394
+ position, // Pass initial intended position for boundary check logic
395
+ initialTop, // Pass offset-adjusted top
396
+ initialLeft, // Pass offset-adjusted left
397
+ targetLayoutRef.current,
398
+ popoverLayoutRef.current,
399
+ screenHeight,
400
+ screenWidth,
401
+ effectiveArrowSize,
402
+ );
403
+
404
+ actualPositionRef.current = finalPosition; // Store the actual position after adjustments
405
+
406
+ setCalculatedPosition({
407
+ position: 'absolute',
408
+ top: finalTop,
409
+ left: finalLeft,
410
+ opacity: 1,
411
+ });
412
+ }, [position, align, showArrow, arrowSize, offset]); // Add offset to dependency array
413
+
414
+ const handlePopoverLayout = useCallback(
415
+ (event: { nativeEvent: { layout: LayoutRectangle } }) => {
416
+ const layout = event.nativeEvent.layout;
417
+ if (layout.width > 0 && layout.height > 0) {
418
+ const changed =
419
+ !popoverLayoutRef.current ||
420
+ popoverLayoutRef.current.width !== layout.width ||
421
+ popoverLayoutRef.current.height !== layout.height;
422
+ if (changed) {
423
+ popoverLayoutRef.current = layout;
424
+ // Trigger recalculation after popover layout is known/updated
425
+ calculateAndSetPosition();
426
+ }
427
+ }
428
+ },
429
+ [calculateAndSetPosition],
430
+ );
431
+
432
+ // Effect to recalculate position if relevant props change while open
433
+ useEffect(() => {
434
+ if (isOpen) {
435
+ calculateAndSetPosition();
436
+ }
437
+ // This effect specifically handles prop changes
438
+ }, [isOpen, position, align, arrowSize, showArrow, calculateAndSetPosition]);
439
+
440
+ // Effect to reset layout refs when popover is closed
441
+ useEffect(() => {
442
+ if (!isOpen) {
443
+ targetLayoutRef.current = null;
444
+ popoverLayoutRef.current = null;
445
+ // Optionally reset calculatedPosition here too, though the initial measure effect also handles hiding
446
+ setCalculatedPosition(popoverDefaultStyles);
447
+ }
448
+ }, [isOpen]);
449
+
450
+ return {
451
+ popoverLayoutRef,
452
+ targetLayoutRef,
453
+ actualPositionRef,
454
+ calculatedPosition,
455
+ calculateAndSetPosition, // Expose for measureTarget
456
+ handlePopoverLayout, // Expose for onLayout prop
457
+ setCalculatedPosition, // Expose for direct hiding if needed
458
+ };
459
+ };
@@ -0,0 +1,2 @@
1
+ export type { Align, PopoverProps, Position } from './common';
2
+ export { default as Popover } from './Popover';
@@ -0,0 +1,13 @@
1
+ import { Portal as GorhomPortal } from '@gorhom/portal';
2
+ import { type ComponentType, type ReactNode } from 'react';
3
+
4
+ import { createContextBridge } from '../../context-bridge';
5
+
6
+ const { BridgedComponent: Portal, registerContextToBridge: registerPortalContext } =
7
+ createContextBridge<Omit<any, 'children'> & { children: ReactNode }>(
8
+ 'portal-context',
9
+ GorhomPortal as ComponentType<any>,
10
+ );
11
+
12
+ export { registerPortalContext };
13
+ export default Portal;
@@ -0,0 +1,12 @@
1
+ import { getRegisteredComponentWithFallback, registerMoleculesComponents } from '../../core';
2
+ import PortalDefault from './Portal';
3
+
4
+ registerMoleculesComponents({
5
+ Portal: PortalDefault,
6
+ });
7
+
8
+ // @ts-ignore TODO - fix this error
9
+ export const Portal = getRegisteredComponentWithFallback('Portal', PortalDefault);
10
+
11
+ export { registerPortalContext } from './Portal';
12
+ export { PortalHost, PortalProvider } from '@gorhom/portal';