react-native-molecules 0.5.0-beta.3 → 0.5.0-beta.30

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 (227) hide show
  1. package/components/Accordion/Accordion.tsx +2 -6
  2. package/components/Accordion/AccordionItem.tsx +16 -12
  3. package/components/Accordion/AccordionItemContent.tsx +6 -1
  4. package/components/Accordion/AccordionItemHeader.tsx +1 -1
  5. package/components/Accordion/utils.ts +6 -0
  6. package/components/ActivityIndicator/ActivityIndicator.tsx +6 -15
  7. package/components/Appbar/AppbarBase.tsx +18 -13
  8. package/components/Button/Button.tsx +211 -264
  9. package/components/Button/index.tsx +9 -3
  10. package/components/Button/types.ts +16 -2
  11. package/components/Button/utils.ts +230 -208
  12. package/components/Card/Card.tsx +1 -1
  13. package/components/Checkbox/Checkbox.tsx +125 -88
  14. package/components/Checkbox/CheckboxBase.ios.tsx +14 -23
  15. package/components/Checkbox/CheckboxBase.tsx +21 -137
  16. package/components/Checkbox/context.tsx +14 -0
  17. package/components/Checkbox/index.tsx +11 -4
  18. package/components/Checkbox/types.ts +63 -29
  19. package/components/Checkbox/utils.ts +25 -108
  20. package/components/Chip/Chip.tsx +41 -52
  21. package/components/Chip/utils.ts +3 -7
  22. package/components/DateField/DateField.tsx +111 -0
  23. package/components/DateField/index.tsx +6 -0
  24. package/components/{DatePickerInput/inputUtils.ts → DateField/useDateFieldState.ts} +19 -51
  25. package/components/DatePicker/DateCalendar.tsx +83 -0
  26. package/components/DatePicker/DatePickerActions.tsx +73 -0
  27. package/components/DatePicker/DatePickerModal.tsx +246 -0
  28. package/components/DatePicker/DatePickerPopover.tsx +79 -0
  29. package/components/DatePicker/DatePickerProvider.tsx +158 -0
  30. package/components/DatePicker/DatePickerTrigger.tsx +23 -0
  31. package/components/DatePicker/context.tsx +83 -0
  32. package/components/DatePicker/index.tsx +45 -0
  33. package/components/DatePicker/utils.ts +295 -0
  34. package/components/DatePickerInline/DatePickerDockedHeader.tsx +117 -0
  35. package/components/DatePickerInline/DatePickerInline.tsx +17 -16
  36. package/components/DatePickerInline/DatePickerInlineBase.tsx +11 -5
  37. package/components/DatePickerInline/DatePickerInlineHeader.tsx +50 -20
  38. package/components/DatePickerInline/Day.tsx +25 -1
  39. package/components/DatePickerInline/DayNames.tsx +13 -10
  40. package/components/DatePickerInline/DayRange.tsx +2 -4
  41. package/components/DatePickerInline/HeaderItem.tsx +44 -29
  42. package/components/DatePickerInline/Month.tsx +48 -67
  43. package/components/DatePickerInline/MonthPicker.tsx +80 -92
  44. package/components/DatePickerInline/Swiper.native.tsx +21 -4
  45. package/components/DatePickerInline/Swiper.tsx +169 -14
  46. package/components/DatePickerInline/SwiperUtils.ts +1 -1
  47. package/components/DatePickerInline/Week.tsx +6 -1
  48. package/components/DatePickerInline/YearPicker.tsx +220 -78
  49. package/components/DatePickerInline/dateUtils.tsx +18 -13
  50. package/components/DatePickerInline/store.tsx +27 -0
  51. package/components/DatePickerInline/types.ts +6 -2
  52. package/components/DatePickerInline/utils.ts +66 -29
  53. package/components/Divider/Divider.tsx +192 -0
  54. package/components/Divider/index.tsx +10 -0
  55. package/components/Drawer/Drawer.tsx +17 -6
  56. package/components/Drawer/DrawerItemGroup.tsx +3 -7
  57. package/components/ElementGroup/ElementGroup.tsx +1 -1
  58. package/components/FilePicker/FilePicker.tsx +48 -78
  59. package/components/FilePicker/index.tsx +2 -1
  60. package/components/FilePicker/utils.ts +9 -0
  61. package/components/HelperText/HelperText.tsx +0 -35
  62. package/components/Icon/iconFactory.tsx +5 -4
  63. package/components/Icon/index.tsx +1 -1
  64. package/components/Icon/types.ts +17 -6
  65. package/components/IconButton/IconButton.tsx +84 -84
  66. package/components/IconButton/index.tsx +1 -0
  67. package/components/IconButton/types.ts +10 -0
  68. package/components/IconButton/utils.ts +167 -33
  69. package/components/List/List.tsx +276 -0
  70. package/components/List/context.tsx +27 -0
  71. package/components/List/index.ts +8 -0
  72. package/components/List/types.ts +117 -0
  73. package/components/List/utils.ts +79 -0
  74. package/components/LoadingIndicator/LoadingIndicator.tsx +253 -0
  75. package/components/LoadingIndicator/LoadingIndicator.web.tsx +136 -0
  76. package/components/LoadingIndicator/index.tsx +13 -0
  77. package/components/LoadingIndicator/utils.ts +117 -0
  78. package/components/Menu/Menu.tsx +162 -39
  79. package/components/Menu/index.tsx +10 -7
  80. package/components/Menu/utils.ts +21 -70
  81. package/components/NavigationRail/NavigationRail.tsx +15 -9
  82. package/components/Popover/Popover.tsx +119 -145
  83. package/components/Popover/PopoverRoot.tsx +60 -0
  84. package/components/Popover/common.ts +54 -34
  85. package/components/Popover/index.ts +12 -1
  86. package/components/Popover/usePlatformMeasure.native.ts +90 -0
  87. package/components/Popover/usePlatformMeasure.ts +120 -0
  88. package/components/Popover/utils.ts +34 -0
  89. package/components/Portal/Portal.tsx +1 -2
  90. package/components/Radio/Radio.tsx +188 -0
  91. package/components/Radio/RadioBase.ios.tsx +69 -0
  92. package/components/Radio/RadioBase.tsx +136 -0
  93. package/components/Radio/context.tsx +23 -0
  94. package/components/Radio/index.tsx +20 -0
  95. package/components/Radio/types.ts +101 -0
  96. package/components/Radio/utils.ts +115 -0
  97. package/components/Rating/Rating.tsx +1 -1
  98. package/components/Select/Select.tsx +521 -785
  99. package/components/Select/context.tsx +81 -0
  100. package/components/Select/index.ts +26 -14
  101. package/components/Select/types.ts +65 -58
  102. package/components/Select/utils.ts +126 -0
  103. package/components/Slot/Slot.tsx +244 -0
  104. package/components/Slot/compose-refs.tsx +62 -0
  105. package/components/Slot/index.tsx +8 -0
  106. package/components/Surface/Surface.android.tsx +32 -7
  107. package/components/Surface/Surface.ios.tsx +34 -29
  108. package/components/Surface/Surface.tsx +31 -4
  109. package/components/Surface/utils.ts +44 -6
  110. package/components/Switch/Switch.ios.tsx +1 -1
  111. package/components/Switch/Switch.tsx +10 -3
  112. package/components/Tabs/TabItem.tsx +35 -58
  113. package/components/Tabs/TabLabel.tsx +5 -9
  114. package/components/Tabs/Tabs.tsx +156 -150
  115. package/components/Tabs/utils.ts +15 -2
  116. package/components/Text/textFactory.tsx +17 -5
  117. package/components/TextInput/TextInput.tsx +663 -579
  118. package/components/TextInput/index.tsx +19 -3
  119. package/components/TextInput/types.ts +77 -28
  120. package/components/TextInput/utils.ts +235 -145
  121. package/components/TimeField/TimeField.tsx +75 -0
  122. package/components/TimeField/index.tsx +6 -0
  123. package/components/TimeField/useTimeFieldState.ts +70 -0
  124. package/components/{TimePickerField/sanitizeTime.ts → TimeField/utils.ts} +77 -10
  125. package/components/TimePicker/AnalogClock.tsx +1 -1
  126. package/components/TimePicker/TimeInput.tsx +87 -42
  127. package/components/TimePicker/TimeInputs.tsx +138 -50
  128. package/components/TimePicker/TimePicker.tsx +74 -11
  129. package/components/TimePicker/TimePickerModal.tsx +186 -0
  130. package/components/TimePicker/context.tsx +17 -0
  131. package/components/TimePicker/index.tsx +15 -3
  132. package/components/TimePicker/utils.ts +93 -4
  133. package/components/Tooltip/Tooltip.tsx +42 -67
  134. package/components/Tooltip/TooltipContent.tsx +32 -5
  135. package/components/Tooltip/TooltipTrigger.tsx +20 -20
  136. package/components/Tooltip/index.tsx +1 -1
  137. package/components/TouchableRipple/TouchableRipple.native.tsx +83 -16
  138. package/components/TouchableRipple/TouchableRipple.tsx +150 -102
  139. package/components/TouchableRipple/rippleFromForegroundColor.ts +21 -0
  140. package/hocs/index.tsx +1 -1
  141. package/hocs/withKeyboardAccessibility.tsx +2 -3
  142. package/hocs/withPortal.tsx +1 -1
  143. package/hooks/index.tsx +2 -12
  144. package/hooks/useActionState.tsx +19 -8
  145. package/hooks/useContrastColor.ts +1 -2
  146. package/hooks/useFilePicker.tsx +7 -17
  147. package/hooks/useHandleNumberFormat.tsx +2 -2
  148. package/hooks/useMediaQuery.tsx +1 -2
  149. package/package.json +95 -111
  150. package/shortcuts-manager/ShortcutsManager/ShortcutsManager.tsx +6 -3
  151. package/shortcuts-manager/ShortcutsManager/utils.tsx +1 -1
  152. package/shortcuts-manager/useSetScopes/useSetScopes.tsx +1 -1
  153. package/shortcuts-manager/useShortcut/useShortcut.tsx +1 -1
  154. package/styles/shadow.ts +2 -1
  155. package/styles/themes/LightTheme.tsx +1 -1
  156. package/utils/DocumentPicker/documentPicker.ts +78 -27
  157. package/utils/DocumentPicker/types.ts +0 -1
  158. package/utils/extractSubcomponents.ts +89 -0
  159. package/utils/extractTextStyles.ts +1 -2
  160. package/utils/formatNumberWithMask/formatNumberWithMask.ts +2 -1
  161. package/utils/index.ts +0 -3
  162. package/utils/normalizeToNumberString/normalizeToNumberString.ts +1 -1
  163. package/components/DatePickerDocked/DatePickerDocked.tsx +0 -30
  164. package/components/DatePickerDocked/DatePickerDockedHeader.tsx +0 -129
  165. package/components/DatePickerDocked/index.tsx +0 -17
  166. package/components/DatePickerDocked/types.ts +0 -11
  167. package/components/DatePickerDocked/utils.ts +0 -157
  168. package/components/DatePickerInline/DatePickerContext.tsx +0 -21
  169. package/components/DatePickerInput/DatePickerInput.tsx +0 -139
  170. package/components/DatePickerInput/DatePickerInputModal.tsx +0 -48
  171. package/components/DatePickerInput/DatePickerInputWithoutModal.tsx +0 -77
  172. package/components/DatePickerInput/DateRangeInput.tsx +0 -88
  173. package/components/DatePickerInput/index.tsx +0 -10
  174. package/components/DatePickerInput/types.ts +0 -28
  175. package/components/DatePickerInput/utils.ts +0 -15
  176. package/components/DatePickerModal/AnimatedCrossView.tsx +0 -94
  177. package/components/DatePickerModal/CalendarEdit.tsx +0 -139
  178. package/components/DatePickerModal/DatePickerModal.tsx +0 -85
  179. package/components/DatePickerModal/DatePickerModalContent.tsx +0 -155
  180. package/components/DatePickerModal/DatePickerModalContentHeader.tsx +0 -213
  181. package/components/DatePickerModal/DatePickerModalHeader.tsx +0 -74
  182. package/components/DatePickerModal/DatePickerModalHeaderBackground.tsx +0 -13
  183. package/components/DatePickerModal/index.tsx +0 -16
  184. package/components/DatePickerModal/types.ts +0 -92
  185. package/components/DatePickerModal/utils.ts +0 -122
  186. package/components/DateTimePicker/DateTimePicker.tsx +0 -172
  187. package/components/DateTimePicker/index.tsx +0 -10
  188. package/components/DateTimePicker/utils.ts +0 -12
  189. package/components/HorizontalDivider/HorizontalDivider.tsx +0 -103
  190. package/components/HorizontalDivider/index.tsx +0 -9
  191. package/components/ListItem/ListItem.tsx +0 -136
  192. package/components/ListItem/ListItemDescription.tsx +0 -25
  193. package/components/ListItem/ListItemTitle.tsx +0 -25
  194. package/components/ListItem/index.tsx +0 -14
  195. package/components/ListItem/utils.ts +0 -115
  196. package/components/Menu/MenuDivider.tsx +0 -13
  197. package/components/Menu/MenuItem.tsx +0 -128
  198. package/components/Popover/Popover.native.tsx +0 -185
  199. package/components/RadioButton/RadioButton.tsx +0 -138
  200. package/components/RadioButton/RadioButtonAndroid.tsx +0 -188
  201. package/components/RadioButton/RadioButtonGroup.tsx +0 -98
  202. package/components/RadioButton/RadioButtonIOS.tsx +0 -106
  203. package/components/RadioButton/RadioButtonItem.tsx +0 -232
  204. package/components/RadioButton/index.ts +0 -22
  205. package/components/RadioButton/utils.ts +0 -165
  206. package/components/TimePickerField/TimePickerField.tsx +0 -152
  207. package/components/TimePickerField/index.tsx +0 -10
  208. package/components/TimePickerField/utils.ts +0 -94
  209. package/components/TimePickerModal/TimePickerModal.tsx +0 -115
  210. package/components/TimePickerModal/index.tsx +0 -10
  211. package/components/TimePickerModal/utils.ts +0 -47
  212. package/components/VerticalDivider/VerticalDivider.tsx +0 -100
  213. package/components/VerticalDivider/index.tsx +0 -9
  214. package/context-bridge/index.tsx +0 -87
  215. package/fast-context/index.tsx +0 -190
  216. package/hocs/typedMemo.tsx +0 -5
  217. package/hooks/useControlledValue.tsx +0 -68
  218. package/hooks/useLatest.tsx +0 -9
  219. package/hooks/useMergedRefs.ts +0 -14
  220. package/hooks/usePrevious.ts +0 -13
  221. package/hooks/useSearchable.tsx +0 -74
  222. package/hooks/useSubcomponents.tsx +0 -59
  223. package/hooks/useToggle.tsx +0 -24
  224. package/utils/color.ts +0 -22
  225. package/utils/compare/index.ts +0 -54
  226. package/utils/lodash.ts +0 -49
  227. package/utils/repository.ts +0 -53
@@ -41,6 +41,9 @@ const timePickerStylesDefault = StyleSheet.create(theme => ({
41
41
 
42
42
  const timePickerInputsStylesDefault = StyleSheet.create(theme => ({
43
43
  spaceBetweenInputsAndSwitcher: { width: 12 },
44
+ wrapper: {
45
+ alignItems: 'center',
46
+ },
44
47
  inputContainer: {
45
48
  flexDirection: 'row',
46
49
  alignItems: 'center',
@@ -70,13 +73,30 @@ const timePickerInputsStylesDefault = StyleSheet.create(theme => ({
70
73
  betweenDot: {
71
74
  height: 12,
72
75
  },
76
+ supportingRow: {
77
+ flexDirection: 'row',
78
+ alignItems: 'flex-start',
79
+ width: '100%',
80
+ marginTop: 2,
81
+ },
82
+ supportingSlot: {
83
+ width: 96,
84
+ minHeight: theme.typescale.bodyMedium.lineHeight * 2,
85
+ },
86
+ supportingText: {
87
+ ...theme.typescale.bodyMedium,
88
+ fontSize: 12,
89
+ lineHeight: 16,
90
+ color: theme.colors.onSurfaceVariant,
91
+ textAlign: 'left',
92
+ paddingHorizontal: theme.spacings['1'],
93
+ },
94
+ supportingTextError: {
95
+ color: theme.colors.error,
96
+ },
73
97
  }));
74
98
 
75
99
  const timePickerInputStylesDefault = StyleSheet.create(theme => ({
76
- root: {
77
- rippleColor: theme.colors.onPrimaryContainer,
78
- } as any,
79
-
80
100
  container: {
81
101
  position: 'relative',
82
102
  },
@@ -91,6 +111,10 @@ const timePickerInputStylesDefault = StyleSheet.create(theme => ({
91
111
  color: theme.colors.onSurface,
92
112
  borderRadius: theme.shapes.corner.small,
93
113
 
114
+ _web: {
115
+ outline: 'none',
116
+ },
117
+
94
118
  variants: {
95
119
  state: {
96
120
  highlighted: {
@@ -100,6 +124,20 @@ const timePickerInputStylesDefault = StyleSheet.create(theme => ({
100
124
  },
101
125
  },
102
126
  },
127
+ keyboardInput: {
128
+ borderWidth: 2,
129
+ borderColor: 'transparent',
130
+ },
131
+ keyboardInputHighlighted: {
132
+ borderColor: theme.colors.primary,
133
+ },
134
+ inputError: {
135
+ backgroundColor: theme.colors.errorContainer,
136
+ color: theme.colors.onErrorContainer,
137
+ },
138
+ keyboardInputError: {
139
+ borderColor: theme.colors.error,
140
+ },
103
141
  button: {
104
142
  overflow: 'hidden',
105
143
  borderRadius: theme.shapes.corner.small,
@@ -218,6 +256,53 @@ const timePickerClockMinutesStylesDefault = StyleSheet.create(theme => ({
218
256
  textWhite: { color: '#fff' },
219
257
  }));
220
258
 
259
+ const timePickerModalStylesDefault = StyleSheet.create(theme => ({
260
+ keyboardView: {
261
+ justifyContent: 'center',
262
+ alignItems: 'center',
263
+ flex: 1,
264
+ },
265
+ modalContent: {
266
+ minWidth: 287,
267
+ width: undefined,
268
+ maxWidth: undefined,
269
+ flex: undefined,
270
+ borderRadius: theme.shapes.corner.extraLarge,
271
+ overflow: 'hidden',
272
+ },
273
+ frame: {
274
+ backgroundColor: theme.colors.surface,
275
+ },
276
+ labelContainer: {
277
+ minHeight: 56,
278
+ justifyContent: 'flex-end',
279
+ paddingLeft: theme.spacings['6'],
280
+ paddingRight: theme.spacings['6'],
281
+ paddingTop: theme.spacings['6'],
282
+ paddingBottom: theme.spacings['2'],
283
+ alignSelf: 'flex-start',
284
+ },
285
+ label: {
286
+ letterSpacing: 1,
287
+ fontSize: theme.typescale.labelLarge.fontSize,
288
+ color: theme.colors.onSurface,
289
+ fontWeight: theme.typescale.labelLarge.fontWeight,
290
+ },
291
+ timePickerContainer: {
292
+ padding: theme.spacings['6'],
293
+ paddingTop: theme.spacings['2'],
294
+ paddingBottom: 0,
295
+ },
296
+ footer: {
297
+ flexDirection: 'row',
298
+ alignItems: 'center',
299
+ padding: theme.spacings['2'],
300
+ width: '100%',
301
+ },
302
+ inputTypeToggle: { margin: theme.spacings['1'] },
303
+ fill: { flex: 1 },
304
+ }));
305
+
221
306
  const timePickerAmPmSwitcherStylesDefault = StyleSheet.create(theme => ({
222
307
  container: {
223
308
  width: 50,
@@ -291,3 +376,7 @@ export const timePickerAmPmSwitcherStyles = getRegisteredComponentStylesWithFall
291
376
  'TimePicker_AmPmSwitcher',
292
377
  timePickerAmPmSwitcherStylesDefault,
293
378
  );
379
+ export const timePickerModalStyles = getRegisteredComponentStylesWithFallback(
380
+ 'TimePickerModal',
381
+ timePickerModalStylesDefault,
382
+ );
@@ -1,42 +1,64 @@
1
+ import { useToggle } from '@react-native-molecules/utils/hooks';
1
2
  import {
2
3
  createContext,
3
4
  memo,
4
5
  type ReactElement,
6
+ type RefObject,
5
7
  useCallback,
6
8
  useEffect,
7
9
  useMemo,
8
10
  useRef,
9
11
  } from 'react';
10
- import { Text, type ViewProps, type ViewStyle } from 'react-native';
11
12
 
12
- import { useSubcomponents, useToggle } from '../../hooks';
13
- import { Popover, type PopoverProps } from '../Popover';
14
- import { tooltipStyles } from './utils';
13
+ import { extractSubcomponents } from '../../utils/extractSubcomponents';
15
14
 
16
- export type Props = Omit<PopoverProps, 'isOpen' | 'triggerRef'> & {
15
+ export type Props = {
17
16
  fadeInDelay?: number;
18
17
  fadeOutDelay?: number;
19
- showArrow?: boolean;
20
- style?: ViewStyle;
21
- children: ReactElement | ReactElement[];
22
18
  hoverableContent?: boolean;
19
+ children: ReactElement | ReactElement[];
20
+ };
21
+
22
+ export type TooltipContextValue = {
23
+ isOpen: boolean;
24
+ triggerRef: RefObject<any>;
25
+ onOpen: () => void;
26
+ onClose: () => void;
27
+ onMouseEnter?: () => void;
28
+ onMouseLeave?: () => void;
23
29
  };
24
30
 
31
+ export const TooltipContext = createContext<TooltipContextValue>({
32
+ isOpen: false,
33
+ onOpen: () => {},
34
+ onClose: () => {},
35
+ triggerRef: { current: null },
36
+ });
37
+
25
38
  const Tooltip = ({
26
- style,
27
39
  children,
28
40
  fadeInDelay = 100,
29
41
  fadeOutDelay = 300,
30
- showArrow = false,
31
42
  hoverableContent = false,
32
- ...rest
33
43
  }: Props) => {
34
44
  const { state: isOpen, setState: setIsOpen } = useToggle(false);
35
45
  const triggerRef = useRef(null);
36
46
  const timeOutRef = useRef<NodeJS.Timeout>(undefined);
37
- const popoverTimeoutRef = useRef<NodeJS.Timeout>(undefined);
38
47
  const preventCloseRef = useRef(false);
39
48
 
49
+ const {
50
+ Tooltip_Trigger,
51
+ Tooltip_Content,
52
+ rest: restChildren,
53
+ } = extractSubcomponents({
54
+ children,
55
+ allowedChildren: [
56
+ { name: 'Tooltip_Trigger', allowMultiple: false },
57
+ { name: 'Tooltip_Content', allowMultiple: false },
58
+ ],
59
+ includeRest: true,
60
+ });
61
+
40
62
  const onClose = useCallback(() => {
41
63
  if (preventCloseRef.current) return;
42
64
  clearTimeout(timeOutRef.current);
@@ -48,34 +70,13 @@ const Tooltip = ({
48
70
  timeOutRef.current = setTimeout(() => setIsOpen(true), fadeInDelay);
49
71
  }, [fadeInDelay, setIsOpen]);
50
72
 
51
- // const setPopoverOpen = useCallback(
52
- // (_isOpen: boolean) => {
53
- // clearTimeout(popoverTimeoutRef.current);
54
- // popoverTimeoutRef.current = setTimeout(
55
- // () => setIsOpen(_isOpen),
56
- // isOpen ? fadeInDelay : fadeOutDelay,
57
- // );
58
- // },
59
- // [fadeInDelay, fadeOutDelay, isOpen, setIsOpen],
60
- // );
61
-
62
- const { Tooltip_Trigger, Tooltip_Content } = useSubcomponents({
63
- children,
64
- allowedChildren: ['Tooltip_Trigger', 'Tooltip_Content'],
65
- });
66
-
67
- const contextValue = useMemo(
73
+ const contextValue = useMemo<TooltipContextValue>(
68
74
  () => ({
75
+ isOpen,
69
76
  triggerRef,
70
77
  onOpen,
71
78
  onClose,
72
- }),
73
- [onClose, onOpen],
74
- );
75
-
76
- const { popoverContentProps, popoverStyle } = useMemo(
77
- () => ({
78
- popoverContentProps: (hoverableContent
79
+ ...(hoverableContent
79
80
  ? {
80
81
  onMouseEnter: () => {
81
82
  preventCloseRef.current = true;
@@ -88,50 +89,24 @@ const Tooltip = ({
88
89
  setIsOpen(false);
89
90
  },
90
91
  }
91
- : {}) as ViewProps,
92
- popoverStyle: [tooltipStyles.content, style],
92
+ : {}),
93
93
  }),
94
- [hoverableContent, setIsOpen, style],
94
+ [hoverableContent, isOpen, onClose, onOpen, setIsOpen],
95
95
  );
96
96
 
97
97
  useEffect(() => {
98
- const popoverTimeout = popoverTimeoutRef;
99
-
100
98
  return () => {
101
99
  clearTimeout(timeOutRef.current);
102
- clearTimeout(popoverTimeout.current);
103
100
  };
104
101
  }, []);
105
102
 
106
103
  return (
107
104
  <TooltipContext.Provider value={contextValue}>
108
- {Tooltip_Trigger[0]}
109
- {isOpen && (
110
- <Popover
111
- isOpen={isOpen}
112
- inverted
113
- // placement={placement}
114
- showArrow={showArrow}
115
- // backdropStyles={styles.backdrop}
116
- triggerRef={triggerRef}
117
- // setIsOpen={setPopoverOpen}
118
- {...popoverContentProps}
119
- {...rest}
120
- style={popoverStyle}
121
- // contentTextStyles={contentTextStyles}
122
- // popoverContentProps={popoverContentProps}
123
- onClose={onClose}>
124
- <Text style={tooltipStyles.contentText}>{Tooltip_Content[0]}</Text>
125
- </Popover>
126
- )}
105
+ {Tooltip_Trigger}
106
+ {Tooltip_Content}
107
+ {restChildren}
127
108
  </TooltipContext.Provider>
128
109
  );
129
110
  };
130
111
 
131
- export const TooltipContext = createContext({
132
- onOpen: () => {},
133
- onClose: () => {},
134
- triggerRef: null as any,
135
- });
136
-
137
112
  export default memo(Tooltip);
@@ -1,11 +1,38 @@
1
- import { memo, type ReactElement, type ReactNode } from 'react';
1
+ import { memo, type ReactNode, useContext, useMemo } from 'react';
2
+ import { type StyleProp, Text, type ViewStyle } from 'react-native';
2
3
 
3
- export type Props = {
4
- children: ReactElement | ReactNode;
4
+ import { Popover, type PopoverProps } from '../Popover';
5
+ import { TooltipContext } from './Tooltip';
6
+ import { tooltipStyles } from './utils';
7
+
8
+ export type Props = Omit<PopoverProps, 'isOpen' | 'triggerRef' | 'onClose' | 'children'> & {
9
+ children?: ReactNode;
5
10
  };
6
11
 
7
- const TooltipContent = memo(({ children }: Props) => {
8
- return <>{children}</>;
12
+ const TooltipContent = memo(({ children, style, ...rest }: Props) => {
13
+ const { isOpen, triggerRef, onClose, onMouseEnter, onMouseLeave } = useContext(TooltipContext);
14
+
15
+ const popoverStyle = useMemo<StyleProp<ViewStyle>>(
16
+ () => [tooltipStyles.content, style],
17
+ [style],
18
+ );
19
+
20
+ if (!isOpen) return null;
21
+
22
+ return (
23
+ <Popover
24
+ isOpen={isOpen}
25
+ inverted
26
+ triggerRef={triggerRef}
27
+ onClose={onClose}
28
+ {...rest}
29
+ style={popoverStyle}
30
+ // @ts-ignore — onMouseEnter/onMouseLeave spread onto the inner View via ...rest in Popover
31
+ onMouseEnter={onMouseEnter}
32
+ onMouseLeave={onMouseLeave}>
33
+ <Text style={tooltipStyles.contentText}>{children}</Text>
34
+ </Popover>
35
+ );
9
36
  });
10
37
 
11
38
  TooltipContent.displayName = 'Tooltip_Content';
@@ -30,27 +30,28 @@ const TooltipTrigger = memo(({ children }: { children: ReactElement }) => {
30
30
  () => triggerRef?.current,
31
31
  );
32
32
 
33
- const onPress = useCallback(() => {
34
- // @ts-ignore
35
- children?.props?.onPress?.();
36
- }, [children?.props]);
37
-
38
- const onLongPress = useCallback(() => {
39
- // @ts-ignore
40
- children?.props?.onLongPress?.();
41
-
42
- if (isWeb) return;
43
- onOpen();
44
- }, [children?.props, isWeb, onOpen]);
33
+ const onLongPress = useCallback(
34
+ (e: unknown) => {
35
+ // @ts-ignore
36
+ children?.props?.onLongPress?.(e);
37
+
38
+ if (isWeb) return;
39
+ onOpen();
40
+ },
41
+ [children?.props, isWeb, onOpen],
42
+ );
45
43
 
46
- const onPressOut = useCallback(() => {
47
- // @ts-ignore
44
+ const onPressOut = useCallback(
45
+ (e: unknown) => {
46
+ // @ts-ignore
48
47
 
49
- children?.props?.onPressOut?.();
48
+ children?.props?.onPressOut?.(e);
50
49
 
51
- if (isWeb) return;
52
- onClose();
53
- }, [children?.props, isWeb, onClose]);
50
+ if (isWeb) return;
51
+ onClose();
52
+ },
53
+ [children?.props, isWeb, onClose],
54
+ );
54
55
 
55
56
  const onHoverIn = useCallback(() => {
56
57
  // @ts-ignore
@@ -84,9 +85,8 @@ const TooltipTrigger = memo(({ children }: { children: ReactElement }) => {
84
85
  ref: actionsRef,
85
86
  onLongPress,
86
87
  onPressOut,
87
- onPress,
88
88
  }),
89
- [children, onLongPress, onPress, onPressOut, actionsRef],
89
+ [children, onLongPress, onPressOut, actionsRef],
90
90
  );
91
91
  });
92
92
 
@@ -10,7 +10,7 @@ export const TooltipDefault = Object.assign(TooltipComponent, {
10
10
 
11
11
  export const Tooltip = getRegisteredComponentWithFallback('Tooltip', TooltipDefault);
12
12
 
13
- export type { Props as TooltipProps } from './Tooltip';
13
+ export type { TooltipContextValue, Props as TooltipProps } from './Tooltip';
14
14
  export type { Props as TooltipContentProps } from './TooltipContent';
15
15
  export type { Props as TooltipTriggerProps } from './TooltipTrigger';
16
16
  export { tooltipStyles } from './utils';
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, memo, useMemo } from 'react';
1
+ import { type ComponentProps, forwardRef, memo, type ReactNode, useMemo } from 'react';
2
2
  import {
3
3
  type BackgroundPropType,
4
4
  Platform,
@@ -7,25 +7,47 @@ import {
7
7
  StyleSheet,
8
8
  TouchableNativeFeedback,
9
9
  TouchableWithoutFeedback,
10
- View,
11
10
  type ViewStyle,
12
11
  } from 'react-native';
13
- import { withUnistyles } from 'react-native-unistyles';
14
12
 
13
+ import { useTheme } from '../../hooks/useTheme';
14
+ import { extractPropertiesFromStyles } from '../../utils/extractPropertiesFromStyles';
15
+ import { Slot } from '../Slot';
16
+ import { rippleColorFromBackground } from './rippleFromForegroundColor';
15
17
  import { touchableRippleStyles } from './utils';
16
18
 
17
19
  const ANDROID_VERSION_LOLLIPOP = 21;
18
20
  const ANDROID_VERSION_PIE = 28;
19
21
 
20
- type Props = React.ComponentProps<typeof TouchableWithoutFeedback> & {
22
+ type Props = ComponentProps<typeof TouchableWithoutFeedback> & {
21
23
  borderless?: boolean;
22
24
  background?: BackgroundPropType;
23
25
  disabled?: boolean;
24
26
  onPress?: () => void | null;
25
27
  rippleColor?: string;
26
28
  underlayColor?: string;
27
- children: React.ReactNode;
29
+ rippleAlpha?: number;
30
+ children: ReactNode;
28
31
  style?: StyleProp<ViewStyle>;
32
+ /**
33
+ * When `true`, the component will not render a wrapper element. Instead, it will
34
+ * merge its props (styles, event handlers, ref) onto its immediate child element.
35
+ * This follows the Radix UI "Slot" pattern for flexible component composition.
36
+ *
37
+ * @note On Android, the native ripple effect will NOT work when `asChild` is `true`
38
+ * because `TouchableNativeFeedback` requires a View wrapper. Only press events will work.
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * <TouchableRipple asChild onPress={handlePress}>
43
+ * <View><Text>Custom pressable</Text></View>
44
+ * </TouchableRipple>
45
+ * ```
46
+ *
47
+ * @note When `asChild` is `true`, only a single child element is allowed.
48
+ * @default false
49
+ */
50
+ asChild?: boolean;
29
51
  };
30
52
 
31
53
  const TouchableRipple = (
@@ -36,28 +58,76 @@ const TouchableRipple = (
36
58
  disabled: disabledProp,
37
59
  rippleColor: rippleColorProp,
38
60
  underlayColor: underlayColorProp,
61
+ rippleAlpha = 0.24,
39
62
  children,
63
+ asChild = false,
40
64
  ...rest
41
65
  }: Props,
42
66
  ref: any,
43
67
  ) => {
44
- const disabled = disabledProp || !rest.onPress;
68
+ const disabled = disabledProp;
69
+ const theme = useTheme();
45
70
 
46
71
  const componentStyles = touchableRippleStyles;
47
72
 
48
73
  const { rippleColor, underlayColor, containerStyle } = useMemo(() => {
74
+ const { rippleColor: themeRippleColor, backgroundColor } = extractPropertiesFromStyles(
75
+ [componentStyles.root, style],
76
+ ['rippleColor', 'backgroundColor'],
77
+ );
78
+ const tokenResolvedColor =
79
+ typeof rippleColorProp === 'string'
80
+ ? theme.colors[rippleColorProp as keyof typeof theme.colors]
81
+ : undefined;
82
+ const rippleColorResolvedProp =
83
+ typeof tokenResolvedColor === 'string' ? tokenResolvedColor : rippleColorProp;
84
+
85
+ const fallback =
86
+ typeof themeRippleColor === 'string' && themeRippleColor.length > 0
87
+ ? themeRippleColor
88
+ : 'rgba(0, 0, 0, 0.32)';
89
+
90
+ const resolvedRipple =
91
+ rippleColorResolvedProp ??
92
+ (backgroundColor != null && backgroundColor !== ''
93
+ ? rippleColorFromBackground(String(backgroundColor), fallback, rippleAlpha)
94
+ : fallback);
95
+
49
96
  return {
50
- rippleColor: rippleColorProp,
51
- underlayColor: underlayColorProp || rippleColorProp,
97
+ rippleColor: resolvedRipple,
98
+ underlayColor: underlayColorProp ?? resolvedRipple,
52
99
  containerStyle: [borderless && styles.borderless, componentStyles.root, style],
53
100
  };
54
- }, [borderless, componentStyles.root, rippleColorProp, style, underlayColorProp]);
101
+ }, [
102
+ borderless,
103
+ componentStyles.root,
104
+ rippleColorProp,
105
+ style,
106
+ underlayColorProp,
107
+ rippleAlpha,
108
+ theme,
109
+ ]);
55
110
 
56
111
  // A workaround for ripple on Android P is to use useForeground + overflow: 'hidden'
57
112
  // https://github.com/facebook/react-native/issues/6480
58
113
  const useForeground =
59
114
  Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_PIE && borderless;
60
115
 
116
+ if (asChild) {
117
+ // When asChild is true, use Slot to merge props with the child
118
+ // Note: TouchableNativeFeedback ripple won't work with asChild since it requires a View wrapper
119
+ return (
120
+ <Slot
121
+ {...rest}
122
+ style={containerStyle}
123
+ ref={ref}
124
+ onPress={rest.onPress}
125
+ disabled={disabled}>
126
+ {children}
127
+ </Slot>
128
+ );
129
+ }
130
+
61
131
  if (TouchableRipple.supported) {
62
132
  return (
63
133
  <TouchableNativeFeedback
@@ -65,12 +135,13 @@ const TouchableRipple = (
65
135
  ref={ref}
66
136
  disabled={disabled}
67
137
  useForeground={useForeground}
138
+ style={containerStyle}
68
139
  background={
69
140
  background != null
70
141
  ? background
71
142
  : TouchableNativeFeedback.Ripple(rippleColor!, borderless)
72
143
  }>
73
- <View style={containerStyle}>{React.Children.only(children)}</View>
144
+ <>{children}</>
74
145
  </TouchableNativeFeedback>
75
146
  );
76
147
  }
@@ -84,7 +155,7 @@ const TouchableRipple = (
84
155
  containerStyle,
85
156
  pressed && { backgroundColor: underlayColor },
86
157
  ]}>
87
- {React.Children.only(children)}
158
+ {children}
88
159
  </Pressable>
89
160
  );
90
161
  };
@@ -98,8 +169,4 @@ const styles = StyleSheet.create({
98
169
  TouchableRipple.supported =
99
170
  Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_LOLLIPOP;
100
171
 
101
- export default memo(
102
- withUnistyles(forwardRef(TouchableRipple), theme => ({
103
- rippleColor: theme.colors.onSurfaceRipple,
104
- })),
105
- );
172
+ export default memo(forwardRef(TouchableRipple));