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

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 (59) hide show
  1. package/components/Button/Button.tsx +3 -1
  2. package/components/Card/Card.tsx +1 -1
  3. package/components/Checkbox/CheckboxBase.ios.tsx +1 -4
  4. package/components/Checkbox/CheckboxBase.tsx +2 -7
  5. package/components/DatePicker/DateCalendar.tsx +4 -4
  6. package/components/DatePicker/DatePickerModal.tsx +2 -1
  7. package/components/DatePicker/utils.ts +2 -0
  8. package/components/DatePickerInline/DatePickerDockedHeader.tsx +3 -3
  9. package/components/DatePickerInline/DatePickerInline.tsx +1 -1
  10. package/components/DatePickerInline/DatePickerInlineBase.tsx +2 -2
  11. package/components/DatePickerInline/DatePickerInlineHeader.tsx +43 -17
  12. package/components/DatePickerInline/HeaderItem.tsx +2 -2
  13. package/components/DatePickerInline/MonthPicker.tsx +58 -64
  14. package/components/DatePickerInline/Swiper.native.tsx +2 -2
  15. package/components/DatePickerInline/Swiper.tsx +3 -3
  16. package/components/DatePickerInline/YearPicker.tsx +108 -119
  17. package/components/DatePickerInline/{DatePickerContext.tsx → store.tsx} +7 -3
  18. package/components/DatePickerInline/types.ts +1 -1
  19. package/components/Divider/Divider.tsx +192 -0
  20. package/components/Divider/index.tsx +11 -0
  21. package/components/Drawer/DrawerItemGroup.tsx +3 -7
  22. package/components/IconButton/IconButton.tsx +2 -12
  23. package/components/List/List.tsx +275 -0
  24. package/components/List/context.tsx +26 -0
  25. package/components/List/index.ts +8 -0
  26. package/components/List/types.ts +117 -0
  27. package/components/List/utils.ts +79 -0
  28. package/components/Menu/Menu.tsx +146 -19
  29. package/components/Menu/index.tsx +9 -7
  30. package/components/Menu/utils.ts +21 -70
  31. package/components/Popover/Popover.tsx +7 -10
  32. package/components/Popover/PopoverRoot.tsx +6 -20
  33. package/components/Popover/common.ts +4 -0
  34. package/components/Popover/index.ts +2 -8
  35. package/components/Popover/usePlatformMeasure.ts +4 -2
  36. package/components/RadioButton/RadioButtonAndroid.tsx +38 -54
  37. package/components/RadioButton/RadioButtonIOS.tsx +2 -16
  38. package/components/Select/Select.tsx +307 -501
  39. package/components/Select/context.tsx +39 -32
  40. package/components/Select/types.ts +63 -56
  41. package/components/Select/utils.ts +19 -44
  42. package/components/Text/textFactory.tsx +17 -5
  43. package/components/TimePicker/TimeInput.tsx +2 -7
  44. package/components/TimePicker/utils.ts +0 -4
  45. package/components/TouchableRipple/TouchableRipple.native.tsx +36 -5
  46. package/components/TouchableRipple/TouchableRipple.tsx +121 -163
  47. package/components/TouchableRipple/rippleFromForegroundColor.ts +21 -0
  48. package/package.json +6 -3
  49. package/components/HorizontalDivider/HorizontalDivider.tsx +0 -103
  50. package/components/HorizontalDivider/index.tsx +0 -9
  51. package/components/ListItem/ListItem.tsx +0 -138
  52. package/components/ListItem/ListItemDescription.tsx +0 -25
  53. package/components/ListItem/ListItemTitle.tsx +0 -25
  54. package/components/ListItem/index.tsx +0 -14
  55. package/components/ListItem/utils.ts +0 -115
  56. package/components/Menu/MenuDivider.tsx +0 -13
  57. package/components/Menu/MenuItem.tsx +0 -128
  58. package/components/VerticalDivider/VerticalDivider.tsx +0 -100
  59. package/components/VerticalDivider/index.tsx +0 -9
@@ -1,44 +1,27 @@
1
1
  import type { View } from 'react-native';
2
2
 
3
3
  import { createFastContext } from '../../fast-context';
4
+ import {
5
+ ListContext,
6
+ ListContextProvider,
7
+ useListContext,
8
+ useListContextValue,
9
+ useListStoreRef,
10
+ } from '../List';
4
11
  import { registerPortalContext } from '../Portal';
5
- import type { DefaultItemT, SelectContextValue, SelectDropdownContextValue } from './types';
6
-
7
- // SelectContext - holds value, onAdd, onRemove with fast-context for optimized rendering
8
- const selectContextDefaultValue: SelectContextValue<DefaultItemT> = {
9
- value: null,
10
- multiple: false,
11
- onAdd: () => {},
12
- onRemove: () => {},
13
- disabled: false,
14
- error: false,
15
- labelKey: 'label',
16
- options: [],
17
- searchQuery: '',
18
- setSearchQuery: () => {},
19
- filteredOptions: [],
20
- };
21
-
22
- const {
23
- useStoreRef: useSelectStoreRef,
24
- Provider: SelectContextProvider,
25
- useContext: useSelectContext,
26
- useContextValue: useSelectContextValue,
27
- Context: SelectContext,
28
- } = createFastContext<SelectContextValue<DefaultItemT>>(selectContextDefaultValue, true);
12
+ import type { DefaultItemT, SelectDropdownContextValue, SelectSearchContextValue } from './types';
29
13
 
30
14
  export {
31
- SelectContext,
32
- SelectContextProvider,
33
- useSelectContext,
34
- useSelectContextValue,
35
- useSelectStoreRef,
15
+ ListContext as SelectContext,
16
+ ListContextProvider as SelectContextProvider,
17
+ useListContext as useSelectContext,
18
+ useListContextValue as useSelectContextValue,
19
+ useListStoreRef as useSelectStoreRef,
36
20
  };
37
21
 
38
22
  // SelectDropdownContext - holds isOpen, onClose, triggerRef with fast-context
39
23
  export type SelectDropdownContextType = SelectDropdownContextValue & {
40
24
  triggerRef: React.RefObject<View> | null;
41
- contentRef: React.RefObject<any> | null;
42
25
  triggerLayout: { width: number; height: number } | null;
43
26
  setTriggerLayout: (layout: { width: number; height: number }) => void;
44
27
  };
@@ -48,7 +31,6 @@ const selectDropdownContextDefaultValue: SelectDropdownContextType = {
48
31
  onClose: () => {},
49
32
  onOpen: () => {},
50
33
  triggerRef: null,
51
- contentRef: null,
52
34
  triggerLayout: null,
53
35
  setTriggerLayout: () => {},
54
36
  };
@@ -61,12 +43,37 @@ const {
61
43
  Context: SelectDropdownContext,
62
44
  } = createFastContext<SelectDropdownContextType>(selectDropdownContextDefaultValue, true);
63
45
 
46
+ const selectSearchContextDefaultValue: SelectSearchContextValue<DefaultItemT> = {
47
+ searchQuery: '',
48
+ setSearchQuery: () => {},
49
+ allOptions: [],
50
+ options: [],
51
+ optionById: new Map(),
52
+ getOptionId: item => item.id,
53
+ };
54
+
55
+ const {
56
+ useStoreRef: useSelectSearchStoreRef,
57
+ Provider: SelectSearchContextProvider,
58
+ useContext: useSelectSearchContext,
59
+ useContextValue: useSelectSearchContextValue,
60
+ Context: SelectSearchContext,
61
+ } = createFastContext<SelectSearchContextValue<DefaultItemT>>(
62
+ selectSearchContextDefaultValue,
63
+ true,
64
+ );
65
+
64
66
  export {
65
67
  SelectDropdownContext,
66
68
  SelectDropdownContextProvider,
69
+ SelectSearchContext,
70
+ SelectSearchContextProvider,
67
71
  useSelectDropdownContext,
68
72
  useSelectDropdownContextValue,
69
73
  useSelectDropdownStoreRef,
74
+ useSelectSearchContext,
75
+ useSelectSearchContextValue,
76
+ useSelectSearchStoreRef,
70
77
  };
71
78
 
72
- registerPortalContext([SelectContext, SelectDropdownContext]);
79
+ registerPortalContext([SelectDropdownContext, SelectSearchContext]);
@@ -1,33 +1,16 @@
1
1
  import type { ComponentType, ReactNode } from 'react';
2
- import type {
3
- GestureResponderEvent,
4
- ScrollViewProps,
5
- TextInputProps,
6
- ViewProps,
7
- } from 'react-native';
2
+ import type { GestureResponderEvent, TextInputProps, ViewProps } from 'react-native';
8
3
 
4
+ import type { ListContentProps, ListItemId, ListValue } from '../List';
9
5
  import type { PopoverProps } from '../Popover';
10
6
 
7
+ export type { ListContextValue as SelectContextValue } from '../List';
8
+
11
9
  export type DefaultItemT = {
12
10
  id: string | number;
13
11
  label?: string;
14
12
  selectable?: boolean;
15
- [key: string]: any;
16
- };
17
-
18
- // SelectContext types
19
- export type SelectContextValue<Option extends DefaultItemT = DefaultItemT> = {
20
- value: Option | Option[] | null;
21
- multiple: boolean;
22
- onAdd: (item: Option) => void;
23
- onRemove: (item: Option) => void;
24
- disabled?: boolean;
25
- error?: boolean;
26
- labelKey?: string;
27
- options: Option[];
28
- searchQuery: string;
29
- setSearchQuery: (query: string) => void;
30
- filteredOptions: Option[];
13
+ [key: string]: unknown;
31
14
  };
32
15
 
33
16
  // SelectDropdownContext types
@@ -37,24 +20,61 @@ export type SelectDropdownContextValue = {
37
20
  onOpen: () => void;
38
21
  };
39
22
 
23
+ export type SelectSearchMode = 'client' | 'external';
24
+
25
+ export type SelectSearchKey<Option extends object = DefaultItemT> =
26
+ | string
27
+ | string[]
28
+ | ((item: Option, query: string) => boolean);
29
+
30
+ export type SelectSearchContextValue<Option extends DefaultItemT = DefaultItemT> = {
31
+ searchQuery: string;
32
+ setSearchQuery: (query: string) => void;
33
+ allOptions: Option[];
34
+ options: Option[];
35
+ optionById: Map<ListItemId, Option>;
36
+ getOptionId: (item: Option) => ListItemId;
37
+ };
38
+
40
39
  // SelectProvider props
41
- export type SelectProps<Option extends DefaultItemT = DefaultItemT> = {
40
+ type SelectPropsBase<Option extends DefaultItemT = DefaultItemT> = {
42
41
  children: ReactNode;
43
- value?: Option['id'] | Option['id'][] | null;
44
- defaultValue?: Option['id'] | Option['id'][] | null;
45
- onChange?: (
46
- value: Option['id'] | Option['id'][] | null,
47
- item: Option,
48
- event?: GestureResponderEvent,
49
- ) => void;
50
- multiple?: boolean;
51
42
  disabled?: boolean;
52
43
  error?: boolean;
53
- labelKey?: string;
54
44
  options: Option[];
55
- searchKey?: string;
45
+ searchKey?: SelectSearchKey<Option>;
46
+ searchQuery?: string;
47
+ defaultSearchQuery?: string;
56
48
  onSearchChange?: (query: string) => void;
57
- hideSelected?: boolean;
49
+ searchMode?: SelectSearchMode;
50
+ allowDeselect?: boolean;
51
+ getItemId?: (item: Option) => ListItemId;
52
+ };
53
+
54
+ export type SelectSearchInputProps = Omit<TextInputProps, 'value' | 'onChangeText'>;
55
+
56
+ type SingleSelectProps<Option extends DefaultItemT = DefaultItemT> = {
57
+ multiple?: false | undefined;
58
+ value?: ListValue<false>;
59
+ defaultValue?: ListValue<false>;
60
+ onChange?: (value: ListValue<false>, item: Option, event?: GestureResponderEvent) => void;
61
+ };
62
+
63
+ type MultipleSelectProps<Option extends DefaultItemT = DefaultItemT> = {
64
+ multiple: true;
65
+ value?: ListValue<true>;
66
+ defaultValue?: ListValue<true>;
67
+ onChange?: (value: ListValue<true>, item: Option, event?: GestureResponderEvent) => void;
68
+ };
69
+
70
+ export type SelectProps<Option extends DefaultItemT = DefaultItemT> = SelectPropsBase<Option> &
71
+ (SingleSelectProps<Option> | MultipleSelectProps<Option>);
72
+
73
+ export type SelectContentProps<Option extends DefaultItemT = DefaultItemT> = Omit<
74
+ ListContentProps,
75
+ 'children'
76
+ > & {
77
+ children?: ReactNode | ((item: Option, isSelected: boolean) => ReactNode);
58
78
  };
59
79
 
60
80
  // Select.Trigger props
@@ -65,6 +85,7 @@ export type SelectTriggerProps = ViewProps & {
65
85
  // Select.Value props
66
86
  export type SelectValueProps = ViewProps & {
67
87
  placeholder?: string;
88
+ labelKey?: string;
68
89
  renderValue?: (value: DefaultItemT | DefaultItemT[] | null) => ReactNode;
69
90
  };
70
91
 
@@ -78,36 +99,22 @@ export type SelectDropdownProps = Omit<
78
99
  wrapperComponentProps?: Record<string, any>;
79
100
  };
80
101
 
81
- // Select.Content props
82
- export type SelectContentProps<Option extends DefaultItemT = DefaultItemT> = Omit<
83
- ScrollViewProps,
84
- 'children'
85
- > & {
86
- children: (item: Option, isSelected: boolean) => ReactNode;
87
- ContainerComponent?: ComponentType<any>;
88
- emptyState?: ReactNode;
89
- };
90
-
91
- // Select.Group props
92
- export type SelectGroupProps = ViewProps & {
93
- children: ReactNode;
94
- label?: string;
95
- };
96
-
97
102
  // Select.Option props
98
- export type SelectOptionProps<Option extends DefaultItemT = DefaultItemT> = ViewProps & {
103
+ // `accessibilityRole` and `role` are intentionally omitted: Select.Option forces them to
104
+ // "option" on web so the dropdown's keyboard navigator (which queries [role="option"]) can
105
+ // always find the rows. Allowing callers to override would silently break keyboard nav.
106
+ export type SelectOptionProps<Option extends DefaultItemT = DefaultItemT> = Omit<
107
+ ViewProps,
108
+ 'accessibilityRole' | 'role'
109
+ > & {
99
110
  /**
100
111
  * Unique id for the option
101
112
  */
102
113
  value: Option['id'];
103
- children?: ReactNode;
104
- renderItem?: (item: Option, isSelected: boolean) => ReactNode;
114
+ children: ReactNode;
105
115
  onPress?: (item: Option, event: GestureResponderEvent) => void;
106
116
  /**
107
117
  * When true, the option can't be selected (similar to HTML option disabled)
108
118
  */
109
119
  disabled?: boolean;
110
120
  };
111
-
112
- // Select.SearchInput props
113
- export type SelectSearchInputProps = Omit<TextInputProps, 'value' | 'onChangeText'> & {};
@@ -2,6 +2,21 @@ import { StyleSheet } from 'react-native-unistyles';
2
2
 
3
3
  import { getRegisteredComponentStylesWithFallback } from '../../core';
4
4
 
5
+ /** Web-only marker on Select.Option roots so keyboard nav does not depend on role/accessibilityRole overrides. */
6
+ export const SELECT_OPTION_DATA_ATTR = 'data-molecules-select-option';
7
+
8
+ const SELECT_OPTION_SELECTOR = `[${SELECT_OPTION_DATA_ATTR}], [data-option-id], [role="option"]`;
9
+
10
+ export function collectWebSelectKeyboardOptionElements(container: ParentNode): HTMLElement[] {
11
+ return Array.from(container.querySelectorAll(SELECT_OPTION_SELECTOR)).filter(
12
+ (el): el is HTMLElement => {
13
+ if (!(el instanceof HTMLElement)) return false;
14
+ if (el.getAttribute('aria-disabled') === 'true') return false;
15
+ return true;
16
+ },
17
+ );
18
+ }
19
+
5
20
  const triggerDefaultStyles = StyleSheet.create(theme => ({
6
21
  trigger: {
7
22
  borderRadius: theme.shapes.corner.extraSmall,
@@ -77,45 +92,15 @@ const triggerDefaultStyles = StyleSheet.create(theme => ({
77
92
  }));
78
93
 
79
94
  export const defaultStyles = StyleSheet.create(theme => ({
95
+ valueText: {
96
+ flex: 1,
97
+ },
80
98
  chipContainer: {
81
99
  flexDirection: 'row',
82
100
  flexWrap: 'wrap',
83
101
  gap: 6,
84
102
  maxWidth: '90%',
85
- },
86
- groupLabel: {
87
- paddingHorizontal: theme.spacings['4'],
88
- paddingVertical: theme.spacings['2'],
89
- fontWeight: '600',
90
- color: theme.colors.onSurface,
91
- },
92
- item: {
93
- paddingHorizontal: theme.spacings['4'],
94
- paddingVertical: theme.spacings['3'],
95
- backgroundColor: 'transparent',
96
-
97
- _web: {
98
- cursor: 'pointer',
99
- outlineStyle: 'none',
100
- _hover: {
101
- backgroundColor: theme.colors.stateLayer.hover.primary,
102
- },
103
- _focus: {
104
- backgroundColor: theme.colors.stateLayer.focussed.primary,
105
- },
106
- },
107
- },
108
- itemSelected: {
109
- backgroundColor: theme.colors.stateLayer.hover.primary,
110
- },
111
- itemDisabled: {
112
- opacity: 0.38,
113
- _web: {
114
- cursor: 'not-allowed',
115
- },
116
- },
117
- itemDisabledText: {
118
- color: theme.colors.onSurfaceVariant,
103
+ flex: 1,
119
104
  },
120
105
  searchInput: {
121
106
  marginHorizontal: theme.spacings['2'],
@@ -124,16 +109,6 @@ export const defaultStyles = StyleSheet.create(theme => ({
124
109
  searchInputInput: {
125
110
  height: 42,
126
111
  },
127
- emptyState: {
128
- paddingHorizontal: theme.spacings['4'],
129
- paddingVertical: theme.spacings['6'],
130
- alignItems: 'center',
131
- justifyContent: 'center',
132
- },
133
- emptyStateText: {
134
- color: theme.colors.onSurfaceVariant,
135
- fontSize: 14,
136
- },
137
112
  }));
138
113
 
139
114
  export const triggerStyles = getRegisteredComponentStylesWithFallback(
@@ -1,6 +1,8 @@
1
1
  import { type ComponentType, createContext, forwardRef, memo, useContext } from 'react';
2
2
  import { Text, type TextProps } from 'react-native';
3
- import { StyleSheet } from 'react-native-unistyles';
3
+ import { StyleSheet, useUnistyles } from 'react-native-unistyles';
4
+
5
+ import { MD3TypescaleKey } from '../../types/theme';
4
6
 
5
7
  const HasAncestorContext = createContext(false);
6
8
 
@@ -8,18 +10,28 @@ const defaultStyles = StyleSheet.create(theme => ({
8
10
  root: { color: theme.colors.onSurface, ...theme.typescale.bodyMedium },
9
11
  }));
10
12
 
13
+ export type TypescaleKey = `${MD3TypescaleKey}`;
14
+
15
+ export type TextFactoryProps = TextProps & {
16
+ typescale?: TypescaleKey;
17
+ };
18
+
11
19
  export const textFactory = (
12
20
  componentStyles: typeof defaultStyles = defaultStyles,
13
21
  isBlockLevelElement = false,
14
22
  DefaultComponent: ComponentType<any> = Text,
15
23
  ) => {
16
24
  return memo(
17
- forwardRef((props: TextProps, ref: any) => {
18
- const { style, ...rest } = props;
25
+ forwardRef((props: TextFactoryProps, ref: any) => {
26
+ const { style, typescale, ...rest } = props;
19
27
  const hasAncestorText = useContext(HasAncestorContext);
28
+ const { theme } = useUnistyles();
29
+
30
+ const typescaleStyle = typescale ? theme.typescale[typescale] : undefined;
20
31
 
21
- const styles =
22
- hasAncestorText && !isBlockLevelElement ? style : [componentStyles?.root, style];
32
+ const baseStyle =
33
+ hasAncestorText && !isBlockLevelElement ? null : componentStyles?.root;
34
+ const styles = [baseStyle, typescaleStyle, style];
23
35
 
24
36
  return hasAncestorText ? (
25
37
  <DefaultComponent ref={ref} style={styles} {...rest} />
@@ -66,7 +66,7 @@ function TimeInput(
66
66
  return str.length === 1 ? `0${str}` : str;
67
67
  }, [value, inputFocused, rawText, error]);
68
68
 
69
- const { rippleColor, containerStyle, textInputStyle, buttonStyle } = useMemo(() => {
69
+ const { containerStyle, textInputStyle, buttonStyle } = useMemo(() => {
70
70
  const {
71
71
  container,
72
72
  input,
@@ -79,7 +79,6 @@ function TimeInput(
79
79
  const isKeyboardInput = inputType === inputTypes.keyboard;
80
80
 
81
81
  return {
82
- rippleColor: timePickerInputStyles.root?._rippleColor,
83
82
  containerStyle: container,
84
83
  textInputStyle: [
85
84
  input,
@@ -146,11 +145,7 @@ function TimeInput(
146
145
  />
147
146
  <>
148
147
  {onPress && inputType === inputTypes.picker ? (
149
- <TouchableRipple
150
- style={buttonStyle}
151
- rippleColor={rippleColor}
152
- onPress={onPressInput}
153
- borderless={true}>
148
+ <TouchableRipple style={buttonStyle} onPress={onPressInput} borderless={true}>
154
149
  <View />
155
150
  </TouchableRipple>
156
151
  ) : null}
@@ -97,10 +97,6 @@ const timePickerInputsStylesDefault = StyleSheet.create(theme => ({
97
97
  }));
98
98
 
99
99
  const timePickerInputStylesDefault = StyleSheet.create(theme => ({
100
- root: {
101
- rippleColor: theme.colors.onPrimaryContainer,
102
- } as any,
103
-
104
100
  container: {
105
101
  position: 'relative',
106
102
  },
@@ -10,8 +10,10 @@ import {
10
10
  type ViewStyle,
11
11
  } from 'react-native';
12
12
 
13
+ import { useTheme } from '../../hooks/useTheme';
13
14
  import { extractPropertiesFromStyles } from '../../utils/extractPropertiesFromStyles';
14
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;
@@ -24,6 +26,7 @@ type Props = ComponentProps<typeof TouchableWithoutFeedback> & {
24
26
  onPress?: () => void | null;
25
27
  rippleColor?: string;
26
28
  underlayColor?: string;
29
+ rippleAlpha?: number;
27
30
  children: ReactNode;
28
31
  style?: StyleProp<ViewStyle>;
29
32
  /**
@@ -55,6 +58,7 @@ const TouchableRipple = (
55
58
  disabled: disabledProp,
56
59
  rippleColor: rippleColorProp,
57
60
  underlayColor: underlayColorProp,
61
+ rippleAlpha = 0.24,
58
62
  children,
59
63
  asChild = false,
60
64
  ...rest
@@ -62,20 +66,47 @@ const TouchableRipple = (
62
66
  ref: any,
63
67
  ) => {
64
68
  const disabled = disabledProp;
69
+ const theme = useTheme();
65
70
 
66
71
  const componentStyles = touchableRippleStyles;
67
72
 
68
73
  const { rippleColor, underlayColor, containerStyle } = useMemo(() => {
69
- const { rippleColor: _rippleColor } = extractPropertiesFromStyles(
74
+ const { rippleColor: themeRippleColor, backgroundColor } = extractPropertiesFromStyles(
70
75
  [componentStyles.root, style],
71
- ['rippleColor'],
76
+ ['rippleColor', 'backgroundColor'],
72
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
+
73
96
  return {
74
- rippleColor: rippleColorProp || _rippleColor,
75
- underlayColor: underlayColorProp || rippleColorProp,
97
+ rippleColor: resolvedRipple,
98
+ underlayColor: underlayColorProp ?? resolvedRipple,
76
99
  containerStyle: [borderless && styles.borderless, componentStyles.root, style],
77
100
  };
78
- }, [borderless, componentStyles.root, rippleColorProp, style, underlayColorProp]);
101
+ }, [
102
+ borderless,
103
+ componentStyles.root,
104
+ rippleColorProp,
105
+ style,
106
+ underlayColorProp,
107
+ rippleAlpha,
108
+ theme,
109
+ ]);
79
110
 
80
111
  // A workaround for ripple on Android P is to use useForeground + overflow: 'hidden'
81
112
  // https://github.com/facebook/react-native/issues/6480