react-native-molecules 0.5.0-beta.2 → 0.5.0-beta.21

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 (160) hide show
  1. package/README.md +1 -1
  2. package/components/Accordion/Accordion.tsx +2 -6
  3. package/components/Accordion/AccordionItem.tsx +16 -12
  4. package/components/Accordion/AccordionItemContent.tsx +6 -1
  5. package/components/Accordion/AccordionItemHeader.tsx +1 -1
  6. package/components/Accordion/utils.ts +6 -0
  7. package/components/ActivityIndicator/ActivityIndicator.tsx +6 -15
  8. package/components/Appbar/AppbarBase.tsx +18 -13
  9. package/components/Button/Button.tsx +209 -264
  10. package/components/Button/index.tsx +9 -3
  11. package/components/Button/types.ts +16 -2
  12. package/components/Button/utils.ts +230 -208
  13. package/components/Checkbox/CheckboxBase.ios.tsx +10 -14
  14. package/components/Checkbox/CheckboxBase.tsx +14 -121
  15. package/components/Checkbox/utils.ts +0 -25
  16. package/components/Chip/Chip.tsx +40 -52
  17. package/components/Chip/utils.ts +3 -7
  18. package/components/DateField/DateField.tsx +111 -0
  19. package/components/DateField/index.tsx +6 -0
  20. package/components/{DatePickerInput/inputUtils.ts → DateField/useDateFieldState.ts} +17 -49
  21. package/components/DatePicker/DateCalendar.tsx +83 -0
  22. package/components/DatePicker/DatePickerActions.tsx +73 -0
  23. package/components/DatePicker/DatePickerModal.tsx +245 -0
  24. package/components/DatePicker/DatePickerPopover.tsx +79 -0
  25. package/components/DatePicker/DatePickerProvider.tsx +158 -0
  26. package/components/DatePicker/DatePickerTrigger.tsx +23 -0
  27. package/components/DatePicker/context.tsx +83 -0
  28. package/components/DatePicker/index.tsx +45 -0
  29. package/components/DatePicker/utils.ts +293 -0
  30. package/components/DatePickerInline/DatePickerContext.tsx +1 -0
  31. package/components/DatePickerInline/DatePickerDockedHeader.tsx +117 -0
  32. package/components/DatePickerInline/DatePickerInline.tsx +16 -15
  33. package/components/DatePickerInline/DatePickerInlineBase.tsx +8 -2
  34. package/components/DatePickerInline/DatePickerInlineHeader.tsx +8 -4
  35. package/components/DatePickerInline/Day.tsx +25 -1
  36. package/components/DatePickerInline/DayNames.tsx +13 -10
  37. package/components/DatePickerInline/DayRange.tsx +2 -4
  38. package/components/DatePickerInline/HeaderItem.tsx +42 -27
  39. package/components/DatePickerInline/Month.tsx +48 -67
  40. package/components/DatePickerInline/MonthPicker.tsx +38 -44
  41. package/components/DatePickerInline/Swiper.native.tsx +21 -4
  42. package/components/DatePickerInline/Swiper.tsx +168 -13
  43. package/components/DatePickerInline/Week.tsx +6 -1
  44. package/components/DatePickerInline/YearPicker.tsx +206 -53
  45. package/components/DatePickerInline/dateUtils.tsx +17 -12
  46. package/components/DatePickerInline/types.ts +6 -2
  47. package/components/DatePickerInline/utils.ts +66 -29
  48. package/components/Drawer/Drawer.tsx +17 -6
  49. package/components/ElementGroup/ElementGroup.tsx +16 -14
  50. package/components/FilePicker/FilePicker.tsx +48 -78
  51. package/components/FilePicker/index.tsx +2 -1
  52. package/components/FilePicker/utils.ts +9 -0
  53. package/components/HelperText/HelperText.tsx +0 -35
  54. package/components/Icon/iconFactory.tsx +3 -3
  55. package/components/Icon/index.tsx +1 -1
  56. package/components/Icon/types.ts +17 -6
  57. package/components/IconButton/IconButton.tsx +42 -57
  58. package/components/IconButton/utils.ts +142 -33
  59. package/components/ListItem/ListItem.tsx +3 -1
  60. package/components/ListItem/utils.ts +1 -1
  61. package/components/LoadingIndicator/LoadingIndicator.tsx +253 -0
  62. package/components/LoadingIndicator/LoadingIndicator.web.tsx +136 -0
  63. package/components/LoadingIndicator/index.tsx +13 -0
  64. package/components/LoadingIndicator/utils.ts +117 -0
  65. package/components/Menu/Menu.tsx +3 -18
  66. package/components/NavigationRail/NavigationRail.tsx +15 -9
  67. package/components/Popover/Popover.tsx +122 -145
  68. package/components/Popover/PopoverRoot.tsx +74 -0
  69. package/components/Popover/common.ts +50 -34
  70. package/components/Popover/index.ts +18 -1
  71. package/components/Popover/usePlatformMeasure.native.ts +90 -0
  72. package/components/Popover/usePlatformMeasure.ts +118 -0
  73. package/components/Popover/utils.ts +34 -0
  74. package/components/Select/Select.tsx +368 -507
  75. package/components/Select/context.tsx +72 -0
  76. package/components/Select/index.ts +8 -14
  77. package/components/Select/types.ts +2 -4
  78. package/components/Select/utils.ts +144 -0
  79. package/components/Slot/Slot.tsx +244 -0
  80. package/components/Slot/compose-refs.tsx +62 -0
  81. package/components/Slot/index.tsx +8 -0
  82. package/components/Surface/Surface.android.tsx +34 -8
  83. package/components/Surface/Surface.ios.tsx +36 -29
  84. package/components/Surface/Surface.tsx +31 -4
  85. package/components/Surface/utils.ts +44 -30
  86. package/components/Switch/Switch.tsx +8 -2
  87. package/components/Tabs/TabItem.tsx +35 -58
  88. package/components/Tabs/TabLabel.tsx +5 -9
  89. package/components/Tabs/Tabs.tsx +154 -148
  90. package/components/Tabs/utils.ts +15 -2
  91. package/components/TextInput/TextInput.tsx +658 -575
  92. package/components/TextInput/index.tsx +19 -3
  93. package/components/TextInput/types.ts +76 -27
  94. package/components/TextInput/utils.ts +225 -145
  95. package/components/TimeField/TimeField.tsx +75 -0
  96. package/components/TimeField/index.tsx +6 -0
  97. package/components/TimeField/useTimeFieldState.ts +70 -0
  98. package/components/{TimePickerField/sanitizeTime.ts → TimeField/utils.ts} +77 -10
  99. package/components/TimePicker/TimeInput.tsx +87 -37
  100. package/components/TimePicker/TimeInputs.tsx +137 -49
  101. package/components/TimePicker/TimePicker.tsx +73 -10
  102. package/components/TimePicker/TimePickerModal.tsx +186 -0
  103. package/components/TimePicker/context.tsx +17 -0
  104. package/components/TimePicker/index.tsx +15 -3
  105. package/components/TimePicker/utils.ts +93 -0
  106. package/components/Tooltip/Tooltip.tsx +42 -67
  107. package/components/Tooltip/TooltipContent.tsx +32 -5
  108. package/components/Tooltip/TooltipTrigger.tsx +20 -20
  109. package/components/Tooltip/index.tsx +1 -1
  110. package/components/TouchableRipple/TouchableRipple.native.tsx +50 -14
  111. package/components/TouchableRipple/TouchableRipple.tsx +137 -47
  112. package/hocs/withPortal.tsx +1 -1
  113. package/hooks/index.tsx +0 -6
  114. package/hooks/useActionState.tsx +19 -8
  115. package/hooks/useControlledValue.tsx +20 -4
  116. package/hooks/useFilePicker.tsx +6 -16
  117. package/hooks/useWhatHasUpdated.tsx +48 -0
  118. package/package.json +17 -13
  119. package/shortcuts-manager/ShortcutsManager/ShortcutsManager.tsx +5 -2
  120. package/styles/shadow.ts +2 -1
  121. package/styles/themes/LightTheme.tsx +1 -1
  122. package/utils/DocumentPicker/documentPicker.ts +78 -27
  123. package/utils/DocumentPicker/types.ts +0 -1
  124. package/utils/extractPropertiesFromStyles.ts +25 -0
  125. package/utils/extractSubcomponents.ts +89 -0
  126. package/utils/lodash.ts +77 -5
  127. package/components/DatePickerDocked/DatePickerDocked.tsx +0 -30
  128. package/components/DatePickerDocked/DatePickerDockedHeader.tsx +0 -129
  129. package/components/DatePickerDocked/index.tsx +0 -17
  130. package/components/DatePickerDocked/types.ts +0 -11
  131. package/components/DatePickerDocked/utils.ts +0 -157
  132. package/components/DatePickerInput/DatePickerInput.tsx +0 -139
  133. package/components/DatePickerInput/DatePickerInputModal.tsx +0 -48
  134. package/components/DatePickerInput/DatePickerInputWithoutModal.tsx +0 -77
  135. package/components/DatePickerInput/DateRangeInput.tsx +0 -88
  136. package/components/DatePickerInput/index.tsx +0 -10
  137. package/components/DatePickerInput/types.ts +0 -28
  138. package/components/DatePickerInput/utils.ts +0 -15
  139. package/components/DatePickerModal/AnimatedCrossView.tsx +0 -94
  140. package/components/DatePickerModal/CalendarEdit.tsx +0 -139
  141. package/components/DatePickerModal/DatePickerModal.tsx +0 -85
  142. package/components/DatePickerModal/DatePickerModalContent.tsx +0 -155
  143. package/components/DatePickerModal/DatePickerModalContentHeader.tsx +0 -213
  144. package/components/DatePickerModal/DatePickerModalHeader.tsx +0 -74
  145. package/components/DatePickerModal/DatePickerModalHeaderBackground.tsx +0 -13
  146. package/components/DatePickerModal/index.tsx +0 -16
  147. package/components/DatePickerModal/types.ts +0 -92
  148. package/components/DatePickerModal/utils.ts +0 -122
  149. package/components/DateTimePicker/DateTimePicker.tsx +0 -172
  150. package/components/DateTimePicker/index.tsx +0 -10
  151. package/components/DateTimePicker/utils.ts +0 -12
  152. package/components/Popover/Popover.native.tsx +0 -185
  153. package/components/TimePickerField/TimePickerField.tsx +0 -152
  154. package/components/TimePickerField/index.tsx +0 -10
  155. package/components/TimePickerField/utils.ts +0 -94
  156. package/components/TimePickerModal/TimePickerModal.tsx +0 -115
  157. package/components/TimePickerModal/index.tsx +0 -10
  158. package/components/TimePickerModal/utils.ts +0 -47
  159. package/hooks/useSearchable.tsx +0 -74
  160. package/hooks/useSubcomponents.tsx +0 -59
@@ -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,6 +73,27 @@ 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 => ({
@@ -91,6 +115,10 @@ const timePickerInputStylesDefault = StyleSheet.create(theme => ({
91
115
  color: theme.colors.onSurface,
92
116
  borderRadius: theme.shapes.corner.small,
93
117
 
118
+ _web: {
119
+ outline: 'none',
120
+ },
121
+
94
122
  variants: {
95
123
  state: {
96
124
  highlighted: {
@@ -100,6 +128,20 @@ const timePickerInputStylesDefault = StyleSheet.create(theme => ({
100
128
  },
101
129
  },
102
130
  },
131
+ keyboardInput: {
132
+ borderWidth: 2,
133
+ borderColor: 'transparent',
134
+ },
135
+ keyboardInputHighlighted: {
136
+ borderColor: theme.colors.primary,
137
+ },
138
+ inputError: {
139
+ backgroundColor: theme.colors.errorContainer,
140
+ color: theme.colors.onErrorContainer,
141
+ },
142
+ keyboardInputError: {
143
+ borderColor: theme.colors.error,
144
+ },
103
145
  button: {
104
146
  overflow: 'hidden',
105
147
  borderRadius: theme.shapes.corner.small,
@@ -218,6 +260,53 @@ const timePickerClockMinutesStylesDefault = StyleSheet.create(theme => ({
218
260
  textWhite: { color: '#fff' },
219
261
  }));
220
262
 
263
+ const timePickerModalStylesDefault = StyleSheet.create(theme => ({
264
+ keyboardView: {
265
+ justifyContent: 'center',
266
+ alignItems: 'center',
267
+ flex: 1,
268
+ },
269
+ modalContent: {
270
+ minWidth: 287,
271
+ width: undefined,
272
+ maxWidth: undefined,
273
+ flex: undefined,
274
+ borderRadius: theme.shapes.corner.extraLarge,
275
+ overflow: 'hidden',
276
+ },
277
+ frame: {
278
+ backgroundColor: theme.colors.surface,
279
+ },
280
+ labelContainer: {
281
+ minHeight: 56,
282
+ justifyContent: 'flex-end',
283
+ paddingLeft: theme.spacings['6'],
284
+ paddingRight: theme.spacings['6'],
285
+ paddingTop: theme.spacings['6'],
286
+ paddingBottom: theme.spacings['2'],
287
+ alignSelf: 'flex-start',
288
+ },
289
+ label: {
290
+ letterSpacing: 1,
291
+ fontSize: theme.typescale.labelLarge.fontSize,
292
+ color: theme.colors.onSurface,
293
+ fontWeight: theme.typescale.labelLarge.fontWeight,
294
+ },
295
+ timePickerContainer: {
296
+ padding: theme.spacings['6'],
297
+ paddingTop: theme.spacings['2'],
298
+ paddingBottom: 0,
299
+ },
300
+ footer: {
301
+ flexDirection: 'row',
302
+ alignItems: 'center',
303
+ padding: theme.spacings['2'],
304
+ width: '100%',
305
+ },
306
+ inputTypeToggle: { margin: theme.spacings['1'] },
307
+ fill: { flex: 1 },
308
+ }));
309
+
221
310
  const timePickerAmPmSwitcherStylesDefault = StyleSheet.create(theme => ({
222
311
  container: {
223
312
  width: 50,
@@ -291,3 +380,7 @@ export const timePickerAmPmSwitcherStyles = getRegisteredComponentStylesWithFall
291
380
  'TimePicker_AmPmSwitcher',
292
381
  timePickerAmPmSwitcherStylesDefault,
293
382
  );
383
+ export const timePickerModalStyles = getRegisteredComponentStylesWithFallback(
384
+ 'TimePickerModal',
385
+ timePickerModalStylesDefault,
386
+ );
@@ -2,41 +2,63 @@ import {
2
2
  createContext,
3
3
  memo,
4
4
  type ReactElement,
5
+ type RefObject,
5
6
  useCallback,
6
7
  useEffect,
7
8
  useMemo,
8
9
  useRef,
9
10
  } from 'react';
10
- import { Text, type ViewProps, type ViewStyle } from 'react-native';
11
11
 
12
- import { useSubcomponents, useToggle } from '../../hooks';
13
- import { Popover, type PopoverProps } from '../Popover';
14
- import { tooltipStyles } from './utils';
12
+ import { useToggle } from '../../hooks';
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,44 @@ 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 { extractPropertiesFromStyles } from '../../utils/extractPropertiesFromStyles';
14
+ import { Slot } from '../Slot';
15
15
  import { touchableRippleStyles } from './utils';
16
16
 
17
17
  const ANDROID_VERSION_LOLLIPOP = 21;
18
18
  const ANDROID_VERSION_PIE = 28;
19
19
 
20
- type Props = React.ComponentProps<typeof TouchableWithoutFeedback> & {
20
+ type Props = ComponentProps<typeof TouchableWithoutFeedback> & {
21
21
  borderless?: boolean;
22
22
  background?: BackgroundPropType;
23
23
  disabled?: boolean;
24
24
  onPress?: () => void | null;
25
25
  rippleColor?: string;
26
26
  underlayColor?: string;
27
- children: React.ReactNode;
27
+ children: ReactNode;
28
28
  style?: StyleProp<ViewStyle>;
29
+ /**
30
+ * When `true`, the component will not render a wrapper element. Instead, it will
31
+ * merge its props (styles, event handlers, ref) onto its immediate child element.
32
+ * This follows the Radix UI "Slot" pattern for flexible component composition.
33
+ *
34
+ * @note On Android, the native ripple effect will NOT work when `asChild` is `true`
35
+ * because `TouchableNativeFeedback` requires a View wrapper. Only press events will work.
36
+ *
37
+ * @example
38
+ * ```tsx
39
+ * <TouchableRipple asChild onPress={handlePress}>
40
+ * <View><Text>Custom pressable</Text></View>
41
+ * </TouchableRipple>
42
+ * ```
43
+ *
44
+ * @note When `asChild` is `true`, only a single child element is allowed.
45
+ * @default false
46
+ */
47
+ asChild?: boolean;
29
48
  };
30
49
 
31
50
  const TouchableRipple = (
@@ -37,17 +56,22 @@ const TouchableRipple = (
37
56
  rippleColor: rippleColorProp,
38
57
  underlayColor: underlayColorProp,
39
58
  children,
59
+ asChild = false,
40
60
  ...rest
41
61
  }: Props,
42
62
  ref: any,
43
63
  ) => {
44
- const disabled = disabledProp || !rest.onPress;
64
+ const disabled = disabledProp;
45
65
 
46
66
  const componentStyles = touchableRippleStyles;
47
67
 
48
68
  const { rippleColor, underlayColor, containerStyle } = useMemo(() => {
69
+ const { rippleColor: _rippleColor } = extractPropertiesFromStyles(
70
+ [componentStyles.root, style],
71
+ ['rippleColor'],
72
+ );
49
73
  return {
50
- rippleColor: rippleColorProp,
74
+ rippleColor: rippleColorProp || _rippleColor,
51
75
  underlayColor: underlayColorProp || rippleColorProp,
52
76
  containerStyle: [borderless && styles.borderless, componentStyles.root, style],
53
77
  };
@@ -58,6 +82,21 @@ const TouchableRipple = (
58
82
  const useForeground =
59
83
  Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_PIE && borderless;
60
84
 
85
+ if (asChild) {
86
+ // When asChild is true, use Slot to merge props with the child
87
+ // Note: TouchableNativeFeedback ripple won't work with asChild since it requires a View wrapper
88
+ return (
89
+ <Slot
90
+ {...rest}
91
+ style={containerStyle}
92
+ ref={ref}
93
+ onPress={rest.onPress}
94
+ disabled={disabled}>
95
+ {children}
96
+ </Slot>
97
+ );
98
+ }
99
+
61
100
  if (TouchableRipple.supported) {
62
101
  return (
63
102
  <TouchableNativeFeedback
@@ -65,12 +104,13 @@ const TouchableRipple = (
65
104
  ref={ref}
66
105
  disabled={disabled}
67
106
  useForeground={useForeground}
107
+ style={containerStyle}
68
108
  background={
69
109
  background != null
70
110
  ? background
71
111
  : TouchableNativeFeedback.Ripple(rippleColor!, borderless)
72
112
  }>
73
- <View style={containerStyle}>{React.Children.only(children)}</View>
113
+ <>{children}</>
74
114
  </TouchableNativeFeedback>
75
115
  );
76
116
  }
@@ -84,7 +124,7 @@ const TouchableRipple = (
84
124
  containerStyle,
85
125
  pressed && { backgroundColor: underlayColor },
86
126
  ]}>
87
- {React.Children.only(children)}
127
+ {children}
88
128
  </Pressable>
89
129
  );
90
130
  };
@@ -98,8 +138,4 @@ const styles = StyleSheet.create({
98
138
  TouchableRipple.supported =
99
139
  Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_LOLLIPOP;
100
140
 
101
- export default memo(
102
- withUnistyles(forwardRef(TouchableRipple), theme => ({
103
- rippleColor: theme.colors.onSurfaceRipple,
104
- })),
105
- );
141
+ export default memo(forwardRef(TouchableRipple));