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
@@ -1,140 +1,92 @@
1
- import setColor from 'color';
1
+ import { forwardRef, memo, type PropsWithoutRef, type ReactNode, useContext, useMemo } from 'react';
2
2
  import {
3
- forwardRef,
4
- memo,
5
- type PropsWithoutRef,
6
- type ReactNode,
7
- useCallback,
8
- useMemo,
9
- } from 'react';
10
- import { type StyleProp, type TextStyle, View, type ViewProps, type ViewStyle } from 'react-native';
3
+ type StyleProp,
4
+ type TextProps,
5
+ type TextStyle,
6
+ type ViewProps,
7
+ type ViewStyle,
8
+ } from 'react-native';
11
9
 
12
10
  import { useActionState } from '../../hooks';
13
11
  import type { MD3Elevation } from '../../types/theme';
14
12
  import { resolveStateVariant } from '../../utils';
15
- import { ActivityIndicator } from '../ActivityIndicator';
16
- import { Icon, type IconType } from '../Icon';
13
+ import { ActivityIndicator, type ActivityIndicatorProps } from '../ActivityIndicator';
14
+ import { Icon, type IconProps } from '../Icon';
17
15
  import { StateLayer } from '../StateLayer';
18
16
  import { Surface, type SurfaceProps } from '../Surface';
19
- import { extractProperties } from '../Surface/utils';
20
17
  import { Text } from '../Text';
21
- import { TouchableRipple } from '../TouchableRipple';
22
- import type { ButtonSize, ButtonVariant } from './types';
23
- import { defaultStyles, sizeToIconSizeMap } from './utils';
18
+ import { TouchableRipple, type TouchableRippleProps } from '../TouchableRipple';
19
+ import type { ButtonContextType, ButtonShape, ButtonSize, ButtonVariant } from './types';
20
+ import {
21
+ buttonActivityIndicatorStyles,
22
+ ButtonContext,
23
+ buttonIconStyles,
24
+ buttonStyles,
25
+ buttonTextStyles,
26
+ elevationMap,
27
+ sizeToIconSizeMap,
28
+ } from './utils';
24
29
 
25
- export type Props = Omit<SurfaceProps, 'style'> & {
26
- /**
27
- * Mode of the button. You can change the mode to adjust the styling to give it desired emphasis.
28
- * - `text` - flat button without background or outline, used for the lowest priority actions, especially when presenting multiple options.
29
- * - `outlined` - button with an outline without background, typically used for important, but not primary action represents medium emphasis.
30
- * - `contained` - button with a background color, used for important action, have the most visual impact and high emphasis.
31
- * - `elevated` - button with a background color and elevation, used when absolutely necessary e.g. button requires visual separation from a patterned background. @supported Available in v5.x with theme version 3
32
- * - `contained-tonal` - button with a secondary background color, an alternative middle ground between contained and outlined buttons. @supported Available in v5.x with theme version 3
33
- */
34
- variant?: ButtonVariant;
35
- /**
36
- * @supported Available in v5.x
37
- * Custom button's background color.
38
- */
39
- buttonColor?: string;
40
- /**
41
- * Whether to show a loading indicator.
42
- */
43
- loading?: boolean;
44
- /**
45
- * Icon to display for the `Button`.
46
- */
47
- iconType?: IconType;
48
- iconName?: string;
49
- iconSize?: number;
50
- /**
51
- * Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch.
52
- */
53
- disabled?: boolean;
54
- /**
55
- * Label text of the button.
56
- */
57
- children: ReactNode;
58
- /**
59
- * Accessibility label for the button. This is read by the screen reader when the user taps the button.
60
- */
61
- accessibilityLabel?: string;
62
- /**
63
- * Accessibility hint for the button. This is read by the screen reader when the user taps the button.
64
- */
65
- accessibilityHint?: string;
66
- /**
67
- * Function to execute on press.
68
- */
69
- onPress?: () => void;
70
- /**
71
- * @supported Available in v5.x
72
- * Function to execute as soon as the touchable element is pressed and invoked even before onPress.
73
- */
74
- onPressIn?: () => void;
75
- /**
76
- * @supported Available in v5.x
77
- * Function to execute as soon as the touch is released even before onPress.
78
- */
79
- onPressOut?: () => void;
80
- /**
81
- * Function to execute on long press.
82
- */
83
- onLongPress?: () => void;
84
- /**
85
- * Style of button's inner content.
86
- * Use this prop to apply custom height and width and to set the icon on the right with `flexDirection: 'row-reverse'`.
87
- */
88
- contentStyle?: StyleProp<ViewStyle>;
89
- style?: StyleProp<TextStyle>;
90
- /**
91
- * Style for the button text.
92
- */
93
- labelStyle?: TextStyle;
94
- /**
95
- * Style for the Icon
96
- */
97
- iconContainerStyle?: StyleProp<ViewStyle>;
98
- iconStyle?: StyleProp<TextStyle>;
99
- /*
100
- * Size
101
- * */
102
- size?: ButtonSize;
103
- /*
104
- * Elevation level
105
- * */
106
- elevation?: MD3Elevation;
107
- /**
108
- * testID to be used on tests.
109
- */
110
- testID?: string;
111
- /**
112
- * props for the stateLayer
113
- */
114
- stateLayerProps?: PropsWithoutRef<ViewProps>;
115
- textRelatedStyle?: StyleProp<TextStyle>;
116
- };
117
-
118
- const elevationMap: Record<string, Record<string, number>> = {
119
- true: {
120
- contained: 1,
121
- 'contained-tonal': 1,
122
- elevated: 2,
123
- },
124
- false: {
125
- elevated: 1,
126
- },
127
- };
30
+ export type Props = Omit<SurfaceProps, 'style'> &
31
+ Pick<TouchableRippleProps, 'onPress' | 'onPressIn' | 'onPressOut' | 'onLongPress'> & {
32
+ /**
33
+ * Mode of the button. You can change the mode to adjust the styling to give it desired emphasis.
34
+ * - `text` - flat button without background or outline, used for the lowest priority actions, especially when presenting multiple options.
35
+ * - `outlined` - button with an outline without background, typically used for important, but not primary action represents medium emphasis.
36
+ * - `contained` - button with a background color, used for important action, have the most visual impact and high emphasis.
37
+ * - `elevated` - button with a background color and elevation, used when absolutely necessary e.g. button requires visual separation from a patterned background. @supported Available in v5.x with theme version 3
38
+ * - `contained-tonal` - button with a secondary background color, an alternative middle ground between contained and outlined buttons. @supported Available in v5.x with theme version 3
39
+ */
40
+ variant?: ButtonVariant;
41
+ /**
42
+ * Shape of the button.
43
+ * - `rounded` - fully rounded corners (default)
44
+ * - `square` - square corners with medium border radius
45
+ */
46
+ shape?: ButtonShape;
47
+ /**
48
+ * Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch.
49
+ */
50
+ disabled?: boolean;
51
+ /**
52
+ * Content of the button. Use Button.Icon and Button.Text compound components.
53
+ */
54
+ children?: ReactNode;
55
+ /**
56
+ * Accessibility label for the button. This is read by the screen reader when the user taps the button.
57
+ */
58
+ accessibilityLabel?: string;
59
+ /**
60
+ * Accessibility hint for the button. This is read by the screen reader when the user taps the button.
61
+ */
62
+ accessibilityHint?: string;
63
+ style?: StyleProp<TextStyle>;
64
+ /*
65
+ * Size
66
+ * */
67
+ size?: ButtonSize;
68
+ /*
69
+ * Elevation level
70
+ * */
71
+ elevation?: MD3Elevation;
72
+ /**
73
+ * testID to be used on tests.
74
+ */
75
+ testID?: string;
76
+ /**
77
+ * props for the stateLayer
78
+ */
79
+ stateLayerProps?: PropsWithoutRef<ViewProps>;
80
+ textRelatedStyle?: StyleProp<TextStyle>;
81
+ disabledPress?: boolean;
82
+ };
128
83
 
129
84
  const Button = (
130
85
  {
131
86
  disabled = false,
132
87
  variant = 'text',
133
- size = 'lg',
134
- loading,
135
- iconType,
136
- iconName,
137
- buttonColor: customButtonColor,
88
+ shape = 'rounded',
89
+ size = 'sm',
138
90
  children,
139
91
  accessibilityLabel,
140
92
  accessibilityHint,
@@ -143,17 +95,13 @@ const Button = (
143
95
  onPressOut,
144
96
  onLongPress,
145
97
  style: styleProp,
146
- contentStyle,
147
- labelStyle,
148
- iconContainerStyle: iconContainerStyleProp,
149
98
  testID,
150
99
  accessible,
151
100
  stateLayerProps = {},
152
101
  elevation: elevationProp,
153
- iconSize: _iconSizeProp,
154
102
  textRelatedStyle,
155
- iconStyle: _iconStyleProp,
156
- ...rest
103
+ disabledPress,
104
+ ...restProps
157
105
  }: Props,
158
106
  ref: any,
159
107
  ) => {
@@ -164,124 +112,48 @@ const Button = (
164
112
  hovered,
165
113
  });
166
114
 
167
- defaultStyles.useVariants({
115
+ buttonStyles.useVariants({
168
116
  variant,
169
117
  // @ts-ignore // TODO - fix this
170
118
  state: state as any,
171
119
  size,
120
+ shape,
172
121
  });
173
-
174
- // const componentStyles = useComponentStyles(
175
- // 'Button',
176
- // [styleProp, { customButtonColor, customTextColor }],
177
- // {
178
- // variant,
179
- // state: disabled ? 'disabled' : hovered ? 'hovered' : undefined,
180
- // size,
181
- // },
182
- // );
183
-
184
- // console.log({ hovered, componentStyles });
185
-
186
- const isVariant = useCallback(
187
- (variantComponent: ButtonVariant) => {
188
- return variant === variantComponent;
189
- },
190
- [variant],
191
- );
192
-
193
- const iconSize = _iconSizeProp ?? sizeToIconSizeMap[size] ?? sizeToIconSizeMap.md;
122
+ const iconSize = sizeToIconSizeMap[size] ?? sizeToIconSizeMap.md;
194
123
  const elevationLevel = elevationMap[(!!hovered).toString()][variant] ?? 0;
195
124
 
196
- const {
197
- customLabelColor,
198
- customLabelSize,
199
- rippleColor,
200
- surfaceStyle,
201
- textStyle,
202
- iconStyle,
203
- viewStyle,
204
- iconContainerStyle,
205
- accessibilityState,
206
- stateLayerStyle,
207
- } = useMemo(() => {
208
- const { button, content, icon, iconTextMode, label, labelText, labelTextAddons } =
209
- defaultStyles;
210
-
211
- // for mobile
212
- const { borderRadius } = extractProperties(
213
- [defaultStyles.root, styleProp],
214
- ['borderRadius'],
215
- );
216
-
217
- const backgroundColor = customButtonColor && !disabled ? customButtonColor : undefined;
218
-
219
- const _iconStyle = [icon, isVariant('text') && iconTextMode];
220
-
221
- const { color: labelColor, fontSize: labelFontSize } = labelStyle ?? {};
222
-
223
- // TODO - remove this workaround
224
- let _rippleColor: string | undefined;
225
-
226
- try {
227
- _rippleColor = setColor(labelColor).alpha(0.12).rgb().string();
228
- } catch (e) {
229
- _rippleColor = undefined;
230
- }
231
-
125
+ const { surfaceStyle, accessibilityState, stateLayerStyle, contextValue } = useMemo(() => {
232
126
  return {
233
- customLabelColor: labelColor,
234
- customLabelSize: labelFontSize,
235
- rippleColor: _rippleColor,
236
- surfaceStyle: [
237
- button,
238
- backgroundColor ? { backgroundColor } : {},
239
- defaultStyles.root,
240
- styleProp,
241
- ],
242
-
243
- iconStyle: [_iconStyle, textRelatedStyle, _iconStyleProp] as unknown as ViewStyle,
244
- viewStyle: [
245
- content,
246
- { flexGrow: 1 },
247
- borderRadius ? { borderRadius } : {},
248
- contentStyle,
249
- ],
250
- iconContainerStyle: [defaultStyles.iconContainer, iconContainerStyleProp],
251
- textStyle: [
252
- // @ts-ignore // TODO - fix this
253
- isVariant('text') ? (iconName || loading ? labelTextAddons : labelText) : label,
254
- textRelatedStyle,
255
- labelStyle,
256
- ],
127
+ surfaceStyle: [buttonStyles.root, styleProp],
257
128
  accessibilityState: { disabled },
258
- stateLayerStyle: [defaultStyles.stateLayer, stateLayerProps?.style],
129
+ stateLayerStyle: [buttonStyles.stateLayer, stateLayerProps?.style],
130
+ contextValue: {
131
+ variant,
132
+ size,
133
+ state: state as ButtonContextType['state'],
134
+ disabled,
135
+ iconSize,
136
+ textRelatedStyle,
137
+ } as ButtonContextType,
259
138
  };
260
- // eslint-disable-next-line
139
+ // eslint-disable-next-line react-hooks/exhaustive-deps
261
140
  }, [
141
+ shape,
262
142
  state,
263
143
  variant,
264
144
  size,
265
- contentStyle,
266
- customButtonColor,
267
145
  disabled,
268
- iconContainerStyleProp,
269
- iconName,
270
- isVariant,
271
- labelStyle,
272
- loading,
273
146
  stateLayerProps?.style,
274
147
  styleProp,
148
+ iconSize,
149
+ textRelatedStyle,
275
150
  ]);
276
151
 
277
- const elevation = useMemo(
278
- () => (elevationProp === undefined ? elevationLevel ?? 0 : elevationProp),
279
- [elevationLevel, elevationProp],
280
- );
152
+ const elevation = elevationProp === undefined ? elevationLevel ?? 0 : elevationProp;
281
153
 
282
154
  return (
283
155
  <Surface
284
- {...rest}
156
+ {...restProps}
285
157
  style={surfaceStyle}
286
158
  elevation={
287
159
  (disabled
@@ -289,7 +161,8 @@ const Button = (
289
161
  : hovered
290
162
  ? (elevationProp || 0) + elevationLevel
291
163
  : elevation) as MD3Elevation
292
- }>
164
+ }
165
+ asChild>
293
166
  <TouchableRipple
294
167
  borderless
295
168
  onPress={onPress}
@@ -301,51 +174,123 @@ const Button = (
301
174
  accessibilityRole="button"
302
175
  accessibilityState={accessibilityState}
303
176
  accessible={accessible}
304
- disabled={disabled}
305
- rippleColor={rippleColor}
306
- style={viewStyle}
177
+ disabled={disabled || disabledPress}
307
178
  ref={actionsRef}
308
179
  testID={testID}>
309
- <>
310
- {iconName && loading !== true ? (
311
- <View style={iconContainerStyle}>
312
- <Icon
313
- type={iconType}
314
- name={iconName}
315
- size={iconSize ?? customLabelSize}
316
- color={
317
- typeof customLabelColor === 'string'
318
- ? customLabelColor
319
- : undefined
320
- }
321
- style={iconStyle}
322
- />
323
- </View>
324
- ) : null}
325
- {loading ? (
326
- <ActivityIndicator
327
- size={customLabelSize ?? iconSize}
328
- color={
329
- (typeof customLabelColor === 'string'
330
- ? customLabelColor
331
- : undefined) as string
332
- }
333
- style={iconStyle}
334
- />
335
- ) : null}
336
- <Text selectable={false} numberOfLines={1} style={textStyle}>
180
+ <ButtonContext.Provider value={contextValue}>
181
+ <>
337
182
  {children}
338
- </Text>
339
183
 
340
- <StateLayer
341
- testID={testID ? `${testID}-stateLayer` : ''}
342
- {...stateLayerProps}
343
- style={stateLayerStyle}
344
- />
345
- </>
184
+ <StateLayer
185
+ testID={testID ? `${testID}-stateLayer` : ''}
186
+ {...stateLayerProps}
187
+ style={stateLayerStyle}
188
+ />
189
+ </>
190
+ </ButtonContext.Provider>
346
191
  </TouchableRipple>
347
192
  </Surface>
348
193
  );
349
194
  };
350
195
 
351
196
  export default memo(forwardRef(Button));
197
+
198
+ /**
199
+ * Button.Icon - Renders an icon within the button
200
+ */
201
+ export const ButtonIcon = memo(
202
+ ({ type, name, size: sizeProp, color: colorProp, style, ...rest }: IconProps) => {
203
+ const { labelColor, iconSize, variant, state, disabled, textRelatedStyle } =
204
+ useContext(ButtonContext);
205
+
206
+ const iconSizeResolved = sizeProp ?? iconSize;
207
+ const colorResolved =
208
+ colorProp ?? (typeof labelColor === 'string' ? labelColor : undefined);
209
+
210
+ buttonIconStyles.useVariants({
211
+ variant,
212
+ // @ts-ignore - state includes 'default' which is valid but not typed
213
+ state,
214
+ });
215
+
216
+ return (
217
+ <Icon
218
+ type={type}
219
+ name={name}
220
+ size={iconSizeResolved}
221
+ color={disabled ? undefined : colorResolved}
222
+ style={[buttonIconStyles.root, textRelatedStyle, style]}
223
+ {...rest}
224
+ />
225
+ );
226
+ },
227
+ );
228
+
229
+ ButtonIcon.displayName = 'Button_Icon';
230
+
231
+ /**
232
+ * Button.Text - Renders text within the button
233
+ */
234
+ export const ButtonText = memo(({ children, style, ...rest }: TextProps) => {
235
+ const { variant, state, size, textRelatedStyle } = useContext(ButtonContext);
236
+
237
+ buttonTextStyles.useVariants({
238
+ variant,
239
+ // @ts-ignore - state includes 'default' which is valid but not typed
240
+ state,
241
+ // @ts-ignore - size type mismatch
242
+ size,
243
+ });
244
+
245
+ return (
246
+ // @ts-ignore - deep type instantiation
247
+ <Text
248
+ selectable={false}
249
+ numberOfLines={1}
250
+ {...rest}
251
+ style={[buttonTextStyles.root, textRelatedStyle, style]}>
252
+ {children}
253
+ </Text>
254
+ );
255
+ });
256
+
257
+ ButtonText.displayName = 'Button_Text';
258
+
259
+ /**
260
+ * Button.Loading - Renders a loading indicator within the button
261
+ */
262
+ export const ButtonActivityIndicator = memo(
263
+ ({
264
+ size: sizeProp,
265
+ color: colorProp,
266
+ style,
267
+ ...rest
268
+ }: Omit<ActivityIndicatorProps, 'animating'>) => {
269
+ const { iconSize, variant, state } = useContext(ButtonContext);
270
+
271
+ const sizeResolved = sizeProp ?? iconSize;
272
+ // Default to onPrimary for contained variants, primary for others
273
+ const colorResolved = colorProp ?? (variant === 'contained' ? 'onPrimary' : 'primary');
274
+
275
+ buttonActivityIndicatorStyles.useVariants({
276
+ variant,
277
+ // @ts-ignore - state includes 'default' which is valid but not typed
278
+ state,
279
+ });
280
+ const activityIndicatorStyle = useMemo(() => {
281
+ return [buttonActivityIndicatorStyles.root, style] as StyleProp<ViewStyle>;
282
+ // eslint-disable-next-line react-hooks/exhaustive-deps
283
+ }, [style, variant, state]);
284
+
285
+ return (
286
+ <ActivityIndicator
287
+ size={sizeResolved}
288
+ color={colorResolved}
289
+ style={activityIndicatorStyle}
290
+ {...rest}
291
+ />
292
+ );
293
+ },
294
+ );
295
+
296
+ ButtonActivityIndicator.displayName = 'Button_ActivityIndicator';
@@ -1,7 +1,13 @@
1
1
  import { getRegisteredComponentWithFallback } from '../../core';
2
- import ButtonDefault from './Button';
2
+ import ButtonDefault, { ButtonActivityIndicator, ButtonIcon, ButtonText } from './Button';
3
3
 
4
- export const Button = getRegisteredComponentWithFallback('Button', ButtonDefault);
4
+ const ButtonBase = getRegisteredComponentWithFallback('Button', ButtonDefault);
5
+
6
+ export const Button = Object.assign(ButtonBase, {
7
+ Icon: ButtonIcon,
8
+ Text: ButtonText,
9
+ ActivityIndicator: ButtonActivityIndicator,
10
+ });
5
11
 
6
12
  export type { Props as ButtonProps } from './Button';
7
- export { defaultStyles } from './utils';
13
+ export { ButtonContext, buttonIconStyles, buttonStyles, buttonTextStyles } from './utils';
@@ -1,5 +1,19 @@
1
+ import type { StyleProp, TextStyle } from 'react-native';
2
+
1
3
  export type ButtonVariant = 'text' | 'outlined' | 'contained' | 'elevated' | 'contained-tonal';
2
4
 
3
- export type ButtonSize = 'sm' | 'md' | 'lg';
5
+ export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
6
+
7
+ export type ButtonStates = 'hovered' | 'disabled' | 'default';
8
+
9
+ export type ButtonShape = 'rounded' | 'square';
4
10
 
5
- export type ButtonStates = 'hovered' | 'disabled';
11
+ export type ButtonContextType = {
12
+ variant: ButtonVariant;
13
+ size: ButtonSize;
14
+ state: ButtonStates;
15
+ disabled: boolean;
16
+ labelColor?: string;
17
+ iconSize?: number;
18
+ textRelatedStyle?: StyleProp<TextStyle>;
19
+ };