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
@@ -3,8 +3,12 @@ import { Animated, type StyleProp, View, type ViewStyle } from 'react-native';
3
3
  import { useUnistyles } from 'react-native-unistyles';
4
4
 
5
5
  import type { MD3Elevation } from '../../types/theme';
6
+ import { extractPropertiesFromStyles } from '../../utils/extractPropertiesFromStyles';
7
+ import { Slot } from '../Slot';
6
8
  import { BackgroundContextWrapper } from './BackgroundContextWrapper';
7
- import { defaultStyles, extractProperties, getStyleForShadowLayer } from './utils';
9
+ import { defaultStyles, getCombinedShadowStyle } from './utils';
10
+
11
+ const AnimatedView = Animated.createAnimatedComponent(View);
8
12
 
9
13
  export type Props = ComponentPropsWithRef<typeof View> & {
10
14
  /**
@@ -26,6 +30,23 @@ export type Props = ComponentPropsWithRef<typeof View> & {
26
30
  * TestID used for testing purposes
27
31
  */
28
32
  testID?: string;
33
+ /**
34
+ * When `true`, the component will not render a wrapper element. Instead, it will
35
+ * merge its props (styles, elevation shadow, ref) onto its immediate child element.
36
+ * This follows the Radix UI "Slot" pattern for flexible component composition.
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ * // With asChild - merges elevation styles onto the child
41
+ * <Surface asChild elevation={2}>
42
+ * <Card><Text>Content</Text></Card>
43
+ * </Surface>
44
+ * ```
45
+ *
46
+ * @note When `asChild` is `true`, only a single child element is allowed.
47
+ * @default false
48
+ */
49
+ asChild?: boolean;
29
50
  };
30
51
 
31
52
  /**
@@ -70,52 +91,38 @@ export type Props = ComponentPropsWithRef<typeof View> & {
70
91
  * });
71
92
  * ```
72
93
  */
73
- const Surface = ({ elevation = 1, style, children, testID, ...props }: Props, ref: any) => {
94
+ const Surface = (
95
+ { elevation = 1, style, children, testID, asChild = false, ...props }: Props,
96
+ ref: any,
97
+ ) => {
74
98
  const { theme } = useUnistyles();
75
99
  const backgroundColor = (() => {
76
100
  // @ts-ignore
77
101
  return theme.colors.elevation?.[`level${elevation}`];
78
102
  })();
79
103
 
80
- const { surfaceBackground, sharedStyle, layer0Style, layer1Style } = useMemo(() => {
81
- const { position, alignSelf, top, left, right, bottom, borderRadius } = extractProperties(
82
- [defaultStyles.root as ViewStyle, style],
83
- ['position', 'alignSelf', 'top', 'left', 'right', 'bottom', 'borderRadius'],
84
- );
85
- const absoluteStyle = { position, alignSelf, top, right, bottom, left };
86
-
104
+ const { surfaceBackground, combinedStyle } = useMemo(() => {
87
105
  return {
88
- surfaceBackground: extractProperties(
106
+ surfaceBackground: extractPropertiesFromStyles(
89
107
  [defaultStyles.root as ViewStyle, style],
90
108
  ['backgroundColor'],
91
109
  ).backgroundColor,
92
- sharedStyle: [
93
- { backgroundColor, borderRadius },
110
+ combinedStyle: [
111
+ { backgroundColor },
112
+ getCombinedShadowStyle(elevation),
94
113
  defaultStyles.root,
95
114
  style,
96
- {
97
- position: undefined,
98
- alignSelf: undefined,
99
- top: undefined,
100
- left: undefined,
101
- right: undefined,
102
- bottom: undefined,
103
- },
104
115
  ],
105
- layer0Style: [getStyleForShadowLayer(0, elevation), absoluteStyle, { borderRadius }],
106
- layer1Style: [getStyleForShadowLayer(1, elevation), { borderRadius }],
107
116
  };
108
117
  }, [backgroundColor, elevation, style]);
109
118
 
119
+ const Component = asChild ? Slot : AnimatedView;
120
+
110
121
  return (
111
122
  <BackgroundContextWrapper backgroundColor={surfaceBackground}>
112
- <View ref={ref} style={layer0Style}>
113
- <View style={layer1Style}>
114
- <View {...props} testID={testID} style={sharedStyle}>
115
- {children}
116
- </View>
117
- </View>
118
- </View>
123
+ <Component ref={ref} {...props} testID={testID} style={combinedStyle}>
124
+ {children}
125
+ </Component>
119
126
  </BackgroundContextWrapper>
120
127
  );
121
128
  };
@@ -1,11 +1,14 @@
1
1
  import { forwardRef, memo, type ReactNode, useMemo } from 'react';
2
- import { type StyleProp, View, type ViewProps, type ViewStyle } from 'react-native';
2
+ import { Animated, type StyleProp, View, type ViewProps, type ViewStyle } from 'react-native';
3
3
 
4
4
  import shadow from '../../styles/shadow';
5
5
  import type { MD3Elevation } from '../../types/theme';
6
+ import { Slot } from '../Slot';
6
7
  import { BackgroundContextWrapper } from './BackgroundContextWrapper';
7
8
  import { defaultStyles } from './utils';
8
9
 
10
+ const AnimatedView = Animated.createAnimatedComponent(View);
11
+
9
12
  export type Props = ViewProps & {
10
13
  /**
11
14
  * Content of the `Surface`.
@@ -18,11 +21,33 @@ export type Props = ViewProps & {
18
21
  * TestID used for testing purposes
19
22
  */
20
23
  testID?: string;
24
+ /**
25
+ * When `true`, the component will not render a wrapper element. Instead, it will
26
+ * merge its props (styles, elevation shadow, ref) onto its immediate child element.
27
+ * This follows the Radix UI "Slot" pattern for flexible component composition.
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * // Without asChild - renders an AnimatedView wrapper
32
+ * <Surface elevation={2}>
33
+ * <Card><Text>Content</Text></Card>
34
+ * </Surface>
35
+ *
36
+ * // With asChild - merges elevation styles onto the child
37
+ * <Surface asChild elevation={2}>
38
+ * <Card><Text>Content</Text></Card>
39
+ * </Surface>
40
+ * ```
41
+ *
42
+ * @note When `asChild` is `true`, only a single child element is allowed.
43
+ * @default false
44
+ */
45
+ asChild?: boolean;
21
46
  };
22
47
 
23
48
  // for Web
24
49
  const Surface = (
25
- { elevation = 1, style, children, testID, backgroundColor, ...props }: Props,
50
+ { elevation = 1, style, children, testID, backgroundColor, asChild = false, ...props }: Props,
26
51
  ref: any,
27
52
  ) => {
28
53
  const { surfaceStyle } = useMemo(() => {
@@ -36,11 +61,13 @@ const Surface = (
36
61
  };
37
62
  }, [backgroundColor, elevation, style]);
38
63
 
64
+ const Component = asChild ? Slot : AnimatedView;
65
+
39
66
  return (
40
67
  <BackgroundContextWrapper backgroundColor={backgroundColor!}>
41
- <View ref={ref} {...props} testID={testID} style={surfaceStyle}>
68
+ <Component ref={ref} {...props} testID={testID} style={surfaceStyle}>
42
69
  {children}
43
- </View>
70
+ </Component>
44
71
  </BackgroundContextWrapper>
45
72
  );
46
73
  };
@@ -15,30 +15,6 @@ export const defaultStyles = getRegisteredComponentStylesWithFallback(
15
15
  defaultStylesDefault,
16
16
  );
17
17
 
18
- // TODO - abstract this
19
- export function extractProperties(
20
- _objectsArray: Record<string, any>,
21
- propertiesToExtract: string[],
22
- ) {
23
- const extracted: Record<string, any> = {};
24
-
25
- const objectsArray = _objectsArray.flat();
26
-
27
- for (let i = objectsArray.length - 1; i >= 0; i--) {
28
- const obj = objectsArray[i];
29
-
30
- for (const prop of propertiesToExtract) {
31
- if (!obj) continue;
32
- if (prop in obj) {
33
- // @ts-ignore
34
- extracted[prop] = obj[prop];
35
- }
36
- }
37
- }
38
-
39
- return extracted;
40
- }
41
-
42
18
  const _shadowColor = '#000';
43
19
 
44
20
  const iOSShadowOutputRanges = [
@@ -96,10 +72,48 @@ export const getStyleForShadowLayer = (
96
72
  };
97
73
  };
98
74
 
99
- export const getElevationAndroid = (
100
- elevation: number,
101
- _inputRange: number[],
102
- elevationLevel: number[],
103
- ) => {
104
- return elevationLevel[elevation];
75
+ /**
76
+ * Combines the two shadow layers into a single shadow style.
77
+ * This approximates the two-layer shadow effect using a single shadow.
78
+ */
79
+ export const getCombinedShadowStyle = (elevation: number, shadowColor = _shadowColor) => {
80
+ if (elevation === 0) {
81
+ return {
82
+ shadowColor,
83
+ shadowOpacity: 0,
84
+ shadowOffset: { width: 0, height: 0 },
85
+ shadowRadius: 0,
86
+ };
87
+ }
88
+
89
+ const layer0 = iOSShadowOutputRanges[0];
90
+ const layer1 = iOSShadowOutputRanges[1];
91
+
92
+ // Use the larger shadow offset (from layer 0)
93
+ const shadowOffsetHeight = layer0.height[elevation];
94
+
95
+ // Use the larger shadow radius (from layer 0)
96
+ const shadowRadius = layer0.shadowRadius[elevation];
97
+
98
+ // Combine opacities (additive, capped at 1.0)
99
+ // This approximates the visual effect of two overlapping shadows
100
+ const shadowOpacity = Math.min(1.0, layer0.shadowOpacity + layer1.shadowOpacity);
101
+
102
+ return {
103
+ shadowColor,
104
+ shadowOpacity,
105
+ shadowOffset: {
106
+ width: 0,
107
+ height: shadowOffsetHeight,
108
+ },
109
+ shadowRadius,
110
+ };
105
111
  };
112
+
113
+ // export const getElevationAndroid = (
114
+ // elevation: number,
115
+ // _inputRange: number[],
116
+ // elevationLevel: number[],
117
+ // ) => {
118
+ // return elevationLevel[elevation];
119
+ // };
@@ -70,6 +70,7 @@ const Switch = (
70
70
  ref,
71
71
  actionsToListen: ['focus', 'hover', 'press'],
72
72
  });
73
+ const isFirstRender = useRef(true);
73
74
 
74
75
  const [value, onChange] = useControlledValue({
75
76
  value: valueProp,
@@ -98,8 +99,8 @@ const Switch = (
98
99
  state,
99
100
  });
100
101
 
101
- const toggleMarginAnimation = useRef(new Animated.Value(value ? 0 : 1)).current;
102
- const toggleSizeAnimation = useRef(new Animated.Value(value ? 0 : 1)).current;
102
+ const toggleMarginAnimation = useRef(new Animated.Value(value ? 1 : 0)).current;
103
+ const toggleSizeAnimation = useRef(new Animated.Value(value ? 1 : 0)).current;
103
104
 
104
105
  const thumbPosition = toggleMarginAnimation.interpolate({
105
106
  inputRange: [0, 1],
@@ -130,6 +131,11 @@ const Switch = (
130
131
  });
131
132
 
132
133
  useEffect(() => {
134
+ if (isFirstRender.current) {
135
+ isFirstRender.current = false;
136
+ return;
137
+ }
138
+
133
139
  Animated.timing(toggleMarginAnimation, {
134
140
  toValue: value ? 1 : 0,
135
141
  duration: 300,
@@ -1,29 +1,20 @@
1
- import {
2
- Children,
3
- cloneElement,
4
- forwardRef,
5
- isValidElement,
6
- type JSXElementConstructor,
7
- memo,
8
- type ReactElement,
9
- useCallback,
10
- useMemo,
11
- useRef,
12
- useState,
13
- } from 'react';
1
+ import { forwardRef, memo, type ReactNode, useCallback, useMemo, useRef, useState } from 'react';
14
2
  import { type LayoutChangeEvent, View, type ViewProps, type ViewStyle } from 'react-native';
15
3
 
16
4
  import { useActionState } from '../../hooks';
17
5
  import { resolveStateVariant } from '../../utils';
18
6
  import { StateLayer } from '../StateLayer';
19
7
  import { TouchableRipple, type TouchableRippleProps } from '../TouchableRipple';
20
- import { tabsItemStyles } from './utils';
8
+ import { TabItemContext, tabsItemStyles } from './utils';
21
9
 
22
- export type TabItemProps = Omit<TouchableRippleProps, 'children' | 'ref'> & {
10
+ export type TabItemProps<T extends string | number> = Omit<
11
+ TouchableRippleProps,
12
+ 'children' | 'ref'
13
+ > & {
23
14
  /**
24
15
  * name of the tab. This should be unique like a route name
25
16
  * */
26
- name: string;
17
+ name: T;
27
18
  /**
28
19
  * Allows to define if TabItem is active.
29
20
  * */
@@ -37,22 +28,11 @@ export type TabItemProps = Omit<TouchableRippleProps, 'children' | 'ref'> & {
37
28
  contentsContainerProps?: Omit<ViewProps, 'children' | 'style' | 'onLayout'>;
38
29
  onLayoutContent?: (e: LayoutChangeEvent) => void;
39
30
  accessibilityLabel?: string;
40
- children: ReactElement<
41
- {
42
- active: boolean;
43
- hovered: boolean;
44
- variant: 'primary' | 'secondary';
45
- },
46
- JSXElementConstructor<{
47
- active: boolean;
48
- hovered: boolean;
49
- variant: 'primary' | 'secondary';
50
- }>
51
- >;
31
+ children: ReactNode;
52
32
  stateLayerProps?: ViewProps;
53
33
  };
54
34
 
55
- const TabItem = (
35
+ const TabItem = <T extends string | number>(
56
36
  {
57
37
  active = false,
58
38
  variant = 'primary',
@@ -65,7 +45,7 @@ const TabItem = (
65
45
  children,
66
46
  stateLayerProps,
67
47
  ...rest
68
- }: TabItemProps,
48
+ }: TabItemProps<T>,
69
49
  ref: any,
70
50
  ) => {
71
51
  const { hovered, actionsRef } = useActionState({ ref, actionsToListen: ['hover'] });
@@ -110,36 +90,33 @@ const TabItem = (
110
90
  [active, accessibilityLabel, state],
111
91
  );
112
92
 
93
+ const contextValue = useMemo(() => ({ active, hovered, variant }), [active, hovered, variant]);
94
+
113
95
  return (
114
- <TouchableRipple
115
- style={containerStyle}
116
- accessibilityRole="tab"
117
- accessibilityState={accessibilityState}
118
- accessibilityValue={accessibilityValue}
119
- {...rest}
120
- ref={actionsRef}
121
- onLayout={onLayout}>
122
- <>
123
- <View
124
- style={[tabsItemStyles.contentsContainer, contentsContainerStyleProp]}
125
- {...contentsContainerProps}
126
- onLayout={onLayoutHandled}>
127
- {Children.map(children, child => {
128
- if (!isValidElement(child)) return null;
129
- return cloneElement(child, {
130
- active,
131
- hovered,
132
- variant,
133
- });
134
- })}
135
- </View>
96
+ <TabItemContext.Provider value={contextValue}>
97
+ <TouchableRipple
98
+ style={containerStyle}
99
+ accessibilityRole="tab"
100
+ accessibilityState={accessibilityState}
101
+ accessibilityValue={accessibilityValue}
102
+ {...rest}
103
+ ref={actionsRef}
104
+ onLayout={onLayout}>
105
+ <>
106
+ <View
107
+ style={[tabsItemStyles.contentsContainer, contentsContainerStyleProp]}
108
+ {...contentsContainerProps}
109
+ onLayout={onLayoutHandled}>
110
+ {children}
111
+ </View>
136
112
 
137
- <StateLayer
138
- {...stateLayerProps}
139
- style={[tabsItemStyles.stateLayer, stateLayerProps?.style]}
140
- />
141
- </>
142
- </TouchableRipple>
113
+ <StateLayer
114
+ {...stateLayerProps}
115
+ style={[tabsItemStyles.stateLayer, stateLayerProps?.style]}
116
+ />
117
+ </>
118
+ </TouchableRipple>
119
+ </TabItemContext.Provider>
143
120
  );
144
121
  };
145
122
 
@@ -1,9 +1,9 @@
1
- import { type FC, memo, useMemo } from 'react';
1
+ import { memo, useContext, useMemo } from 'react';
2
2
  import { type TextProps, type TextStyle } from 'react-native';
3
3
 
4
4
  import { Icon, type IconProps, type IconType } from '../Icon';
5
5
  import { Text } from '../Text';
6
- import { tabsLabelStyles } from './utils';
6
+ import { TabItemContext, tabsLabelStyles } from './utils';
7
7
 
8
8
  const DEFAULT_ICON_SIZE = 24;
9
9
 
@@ -23,11 +23,6 @@ export type TabLabelProps = {
23
23
  iconStyle?: TextStyle;
24
24
 
25
25
  activeColor?: string;
26
-
27
- active: boolean;
28
- hovered: boolean;
29
-
30
- variant: 'primary' | 'secondary';
31
26
  };
32
27
 
33
28
  const TabLabel = memo((props: TabLabelProps) => {
@@ -40,9 +35,10 @@ const TabLabel = memo((props: TabLabelProps) => {
40
35
  labelStyle: labelStyleProp,
41
36
  labelProps,
42
37
  label,
43
- active,
44
38
  } = props;
45
39
 
40
+ const { active } = useContext(TabItemContext);
41
+
46
42
  // tabsLabelStyles.useVariants({
47
43
  // variant,
48
44
  // state: resolveStateVariant({
@@ -81,4 +77,4 @@ const TabLabel = memo((props: TabLabelProps) => {
81
77
 
82
78
  TabLabel.displayName = 'TabLabel';
83
79
 
84
- export default TabLabel as unknown as FC<Omit<TabLabelProps, 'active' | 'hovered' | 'variant'>>;
80
+ export default TabLabel;