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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/components/Accordion/Accordion.tsx +2 -6
  2. package/components/Accordion/AccordionItem.tsx +16 -12
  3. package/components/Accordion/AccordionItemContent.tsx +6 -1
  4. package/components/Accordion/AccordionItemHeader.tsx +1 -1
  5. package/components/Accordion/utils.ts +6 -0
  6. package/components/ActivityIndicator/ActivityIndicator.tsx +6 -15
  7. package/components/Appbar/AppbarBase.tsx +18 -13
  8. package/components/Button/Button.tsx +211 -264
  9. package/components/Button/index.tsx +9 -3
  10. package/components/Button/types.ts +16 -2
  11. package/components/Button/utils.ts +230 -208
  12. package/components/Card/Card.tsx +1 -1
  13. package/components/Checkbox/Checkbox.tsx +125 -88
  14. package/components/Checkbox/CheckboxBase.ios.tsx +14 -23
  15. package/components/Checkbox/CheckboxBase.tsx +21 -137
  16. package/components/Checkbox/context.tsx +14 -0
  17. package/components/Checkbox/index.tsx +11 -4
  18. package/components/Checkbox/types.ts +63 -29
  19. package/components/Checkbox/utils.ts +25 -108
  20. package/components/Chip/Chip.tsx +41 -52
  21. package/components/Chip/utils.ts +3 -7
  22. package/components/DateField/DateField.tsx +111 -0
  23. package/components/DateField/index.tsx +6 -0
  24. package/components/{DatePickerInput/inputUtils.ts → DateField/useDateFieldState.ts} +19 -51
  25. package/components/DatePicker/DateCalendar.tsx +83 -0
  26. package/components/DatePicker/DatePickerActions.tsx +73 -0
  27. package/components/DatePicker/DatePickerModal.tsx +246 -0
  28. package/components/DatePicker/DatePickerPopover.tsx +79 -0
  29. package/components/DatePicker/DatePickerProvider.tsx +158 -0
  30. package/components/DatePicker/DatePickerTrigger.tsx +23 -0
  31. package/components/DatePicker/context.tsx +83 -0
  32. package/components/DatePicker/index.tsx +45 -0
  33. package/components/DatePicker/utils.ts +295 -0
  34. package/components/DatePickerInline/DatePickerDockedHeader.tsx +117 -0
  35. package/components/DatePickerInline/DatePickerInline.tsx +17 -16
  36. package/components/DatePickerInline/DatePickerInlineBase.tsx +11 -5
  37. package/components/DatePickerInline/DatePickerInlineHeader.tsx +50 -20
  38. package/components/DatePickerInline/Day.tsx +25 -1
  39. package/components/DatePickerInline/DayNames.tsx +13 -10
  40. package/components/DatePickerInline/DayRange.tsx +2 -4
  41. package/components/DatePickerInline/HeaderItem.tsx +44 -29
  42. package/components/DatePickerInline/Month.tsx +48 -67
  43. package/components/DatePickerInline/MonthPicker.tsx +80 -92
  44. package/components/DatePickerInline/Swiper.native.tsx +21 -4
  45. package/components/DatePickerInline/Swiper.tsx +169 -14
  46. package/components/DatePickerInline/SwiperUtils.ts +1 -1
  47. package/components/DatePickerInline/Week.tsx +6 -1
  48. package/components/DatePickerInline/YearPicker.tsx +220 -78
  49. package/components/DatePickerInline/dateUtils.tsx +18 -13
  50. package/components/DatePickerInline/store.tsx +27 -0
  51. package/components/DatePickerInline/types.ts +6 -2
  52. package/components/DatePickerInline/utils.ts +66 -29
  53. package/components/Divider/Divider.tsx +192 -0
  54. package/components/Divider/index.tsx +10 -0
  55. package/components/Drawer/Drawer.tsx +17 -6
  56. package/components/Drawer/DrawerItemGroup.tsx +3 -7
  57. package/components/ElementGroup/ElementGroup.tsx +1 -1
  58. package/components/FilePicker/FilePicker.tsx +48 -78
  59. package/components/FilePicker/index.tsx +2 -1
  60. package/components/FilePicker/utils.ts +9 -0
  61. package/components/HelperText/HelperText.tsx +0 -35
  62. package/components/Icon/iconFactory.tsx +5 -4
  63. package/components/Icon/index.tsx +1 -1
  64. package/components/Icon/types.ts +17 -6
  65. package/components/IconButton/IconButton.tsx +84 -84
  66. package/components/IconButton/index.tsx +1 -0
  67. package/components/IconButton/types.ts +10 -0
  68. package/components/IconButton/utils.ts +167 -33
  69. package/components/List/List.tsx +276 -0
  70. package/components/List/context.tsx +27 -0
  71. package/components/List/index.ts +8 -0
  72. package/components/List/types.ts +117 -0
  73. package/components/List/utils.ts +79 -0
  74. package/components/LoadingIndicator/LoadingIndicator.tsx +253 -0
  75. package/components/LoadingIndicator/LoadingIndicator.web.tsx +136 -0
  76. package/components/LoadingIndicator/index.tsx +13 -0
  77. package/components/LoadingIndicator/utils.ts +117 -0
  78. package/components/Menu/Menu.tsx +162 -39
  79. package/components/Menu/index.tsx +10 -7
  80. package/components/Menu/utils.ts +21 -70
  81. package/components/NavigationRail/NavigationRail.tsx +15 -9
  82. package/components/Popover/Popover.tsx +119 -145
  83. package/components/Popover/PopoverRoot.tsx +60 -0
  84. package/components/Popover/common.ts +54 -34
  85. package/components/Popover/index.ts +12 -1
  86. package/components/Popover/usePlatformMeasure.native.ts +90 -0
  87. package/components/Popover/usePlatformMeasure.ts +120 -0
  88. package/components/Popover/utils.ts +34 -0
  89. package/components/Portal/Portal.tsx +1 -2
  90. package/components/Radio/Radio.tsx +188 -0
  91. package/components/Radio/RadioBase.ios.tsx +69 -0
  92. package/components/Radio/RadioBase.tsx +136 -0
  93. package/components/Radio/context.tsx +23 -0
  94. package/components/Radio/index.tsx +20 -0
  95. package/components/Radio/types.ts +101 -0
  96. package/components/Radio/utils.ts +115 -0
  97. package/components/Rating/Rating.tsx +1 -1
  98. package/components/Select/Select.tsx +521 -785
  99. package/components/Select/context.tsx +81 -0
  100. package/components/Select/index.ts +26 -14
  101. package/components/Select/types.ts +65 -58
  102. package/components/Select/utils.ts +126 -0
  103. package/components/Slot/Slot.tsx +224 -0
  104. package/components/Slot/compose-refs.tsx +62 -0
  105. package/components/Slot/index.tsx +8 -0
  106. package/components/Surface/Surface.android.tsx +32 -7
  107. package/components/Surface/Surface.ios.tsx +34 -29
  108. package/components/Surface/Surface.tsx +31 -4
  109. package/components/Surface/utils.ts +44 -6
  110. package/components/Switch/Switch.ios.tsx +1 -1
  111. package/components/Switch/Switch.tsx +10 -3
  112. package/components/Tabs/TabItem.tsx +35 -58
  113. package/components/Tabs/TabLabel.tsx +5 -9
  114. package/components/Tabs/Tabs.tsx +156 -150
  115. package/components/Tabs/utils.ts +15 -2
  116. package/components/Text/textFactory.tsx +17 -5
  117. package/components/TextInput/TextInput.tsx +663 -579
  118. package/components/TextInput/index.tsx +19 -3
  119. package/components/TextInput/types.ts +77 -28
  120. package/components/TextInput/utils.ts +235 -145
  121. package/components/TimeField/TimeField.tsx +75 -0
  122. package/components/TimeField/index.tsx +6 -0
  123. package/components/TimeField/useTimeFieldState.ts +70 -0
  124. package/components/{TimePickerField/sanitizeTime.ts → TimeField/utils.ts} +77 -10
  125. package/components/TimePicker/AnalogClock.tsx +1 -1
  126. package/components/TimePicker/TimeInput.tsx +87 -42
  127. package/components/TimePicker/TimeInputs.tsx +138 -50
  128. package/components/TimePicker/TimePicker.tsx +74 -11
  129. package/components/TimePicker/TimePickerModal.tsx +186 -0
  130. package/components/TimePicker/context.tsx +17 -0
  131. package/components/TimePicker/index.tsx +15 -3
  132. package/components/TimePicker/utils.ts +93 -4
  133. package/components/Tooltip/Tooltip.tsx +42 -67
  134. package/components/Tooltip/TooltipContent.tsx +32 -5
  135. package/components/Tooltip/TooltipTrigger.tsx +21 -24
  136. package/components/Tooltip/index.tsx +1 -1
  137. package/components/TouchableRipple/TouchableRipple.native.tsx +83 -16
  138. package/components/TouchableRipple/TouchableRipple.tsx +150 -102
  139. package/components/TouchableRipple/rippleFromForegroundColor.ts +21 -0
  140. package/hocs/index.tsx +1 -1
  141. package/hocs/withKeyboardAccessibility.tsx +2 -3
  142. package/hocs/withPortal.tsx +1 -1
  143. package/hooks/index.tsx +2 -12
  144. package/hooks/useActionState.tsx +19 -8
  145. package/hooks/useContrastColor.ts +1 -2
  146. package/hooks/useFilePicker.tsx +7 -17
  147. package/hooks/useHandleNumberFormat.tsx +2 -2
  148. package/hooks/useMediaQuery.tsx +1 -2
  149. package/package.json +95 -111
  150. package/shortcuts-manager/ShortcutsManager/ShortcutsManager.tsx +6 -3
  151. package/shortcuts-manager/ShortcutsManager/utils.tsx +1 -1
  152. package/shortcuts-manager/useSetScopes/useSetScopes.tsx +1 -1
  153. package/shortcuts-manager/useShortcut/useShortcut.tsx +1 -1
  154. package/styles/shadow.ts +2 -1
  155. package/styles/themes/LightTheme.tsx +1 -1
  156. package/utils/DocumentPicker/documentPicker.ts +78 -27
  157. package/utils/DocumentPicker/types.ts +0 -1
  158. package/utils/extractSubcomponents.ts +89 -0
  159. package/utils/extractTextStyles.ts +1 -2
  160. package/utils/formatNumberWithMask/formatNumberWithMask.ts +2 -1
  161. package/utils/index.ts +0 -3
  162. package/utils/normalizeToNumberString/normalizeToNumberString.ts +1 -1
  163. package/components/DatePickerDocked/DatePickerDocked.tsx +0 -30
  164. package/components/DatePickerDocked/DatePickerDockedHeader.tsx +0 -129
  165. package/components/DatePickerDocked/index.tsx +0 -17
  166. package/components/DatePickerDocked/types.ts +0 -11
  167. package/components/DatePickerDocked/utils.ts +0 -157
  168. package/components/DatePickerInline/DatePickerContext.tsx +0 -21
  169. package/components/DatePickerInput/DatePickerInput.tsx +0 -139
  170. package/components/DatePickerInput/DatePickerInputModal.tsx +0 -48
  171. package/components/DatePickerInput/DatePickerInputWithoutModal.tsx +0 -77
  172. package/components/DatePickerInput/DateRangeInput.tsx +0 -88
  173. package/components/DatePickerInput/index.tsx +0 -10
  174. package/components/DatePickerInput/types.ts +0 -28
  175. package/components/DatePickerInput/utils.ts +0 -15
  176. package/components/DatePickerModal/AnimatedCrossView.tsx +0 -94
  177. package/components/DatePickerModal/CalendarEdit.tsx +0 -139
  178. package/components/DatePickerModal/DatePickerModal.tsx +0 -85
  179. package/components/DatePickerModal/DatePickerModalContent.tsx +0 -155
  180. package/components/DatePickerModal/DatePickerModalContentHeader.tsx +0 -213
  181. package/components/DatePickerModal/DatePickerModalHeader.tsx +0 -74
  182. package/components/DatePickerModal/DatePickerModalHeaderBackground.tsx +0 -13
  183. package/components/DatePickerModal/index.tsx +0 -16
  184. package/components/DatePickerModal/types.ts +0 -92
  185. package/components/DatePickerModal/utils.ts +0 -122
  186. package/components/DateTimePicker/DateTimePicker.tsx +0 -172
  187. package/components/DateTimePicker/index.tsx +0 -10
  188. package/components/DateTimePicker/utils.ts +0 -12
  189. package/components/HorizontalDivider/HorizontalDivider.tsx +0 -103
  190. package/components/HorizontalDivider/index.tsx +0 -9
  191. package/components/ListItem/ListItem.tsx +0 -136
  192. package/components/ListItem/ListItemDescription.tsx +0 -25
  193. package/components/ListItem/ListItemTitle.tsx +0 -25
  194. package/components/ListItem/index.tsx +0 -14
  195. package/components/ListItem/utils.ts +0 -115
  196. package/components/Menu/MenuDivider.tsx +0 -13
  197. package/components/Menu/MenuItem.tsx +0 -128
  198. package/components/Popover/Popover.native.tsx +0 -185
  199. package/components/RadioButton/RadioButton.tsx +0 -138
  200. package/components/RadioButton/RadioButtonAndroid.tsx +0 -188
  201. package/components/RadioButton/RadioButtonGroup.tsx +0 -98
  202. package/components/RadioButton/RadioButtonIOS.tsx +0 -106
  203. package/components/RadioButton/RadioButtonItem.tsx +0 -232
  204. package/components/RadioButton/index.ts +0 -22
  205. package/components/RadioButton/utils.ts +0 -165
  206. package/components/TimePickerField/TimePickerField.tsx +0 -152
  207. package/components/TimePickerField/index.tsx +0 -10
  208. package/components/TimePickerField/utils.ts +0 -94
  209. package/components/TimePickerModal/TimePickerModal.tsx +0 -115
  210. package/components/TimePickerModal/index.tsx +0 -10
  211. package/components/TimePickerModal/utils.ts +0 -47
  212. package/components/VerticalDivider/VerticalDivider.tsx +0 -100
  213. package/components/VerticalDivider/index.tsx +0 -9
  214. package/context-bridge/index.tsx +0 -87
  215. package/fast-context/index.tsx +0 -190
  216. package/hocs/typedMemo.tsx +0 -5
  217. package/hooks/useControlledValue.tsx +0 -68
  218. package/hooks/useLatest.tsx +0 -9
  219. package/hooks/useMergedRefs.ts +0 -14
  220. package/hooks/usePrevious.ts +0 -13
  221. package/hooks/useSearchable.tsx +0 -74
  222. package/hooks/useSubcomponents.tsx +0 -59
  223. package/hooks/useToggle.tsx +0 -24
  224. package/utils/color.ts +0 -22
  225. package/utils/compare/index.ts +0 -54
  226. package/utils/lodash.ts +0 -49
  227. package/utils/repository.ts +0 -53
@@ -1,15 +1,17 @@
1
- import { forwardRef, memo, type ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
1
+ import { useControlledValue } from '@react-native-molecules/utils/hooks';
2
+ import { memo, useCallback, useMemo } from 'react';
2
3
 
3
4
  import useFilePicker from '../../hooks/useFilePicker';
4
5
  import type { DocumentPickerOptions, DocumentResult } from '../../utils/DocumentPicker';
5
- import { ActivityIndicator } from '../ActivityIndicator';
6
6
  import { IconButton } from '../IconButton';
7
7
  import { TextInput, type TextInputProps } from '../TextInput';
8
+ import { FilePickerContext } from './utils';
8
9
 
9
10
  export type OmitProp =
10
11
  | 'editable'
11
12
  | 'multiline'
12
13
  | 'onChangeText'
14
+ | 'onChange'
13
15
  | 'keyboardType'
14
16
  | 'defaultValue'
15
17
  | 'value'
@@ -24,45 +26,39 @@ export type Props = Omit<TextInputProps, OmitProp> &
24
26
  */
25
27
  multiple?: boolean;
26
28
  /**
27
- * Displays the Spinner on the right side in place of the default upload icon
28
- * @default false
29
+ * Default value for uncontrolled usage
29
30
  */
30
- loading?: boolean;
31
- left?: ReactNode;
32
- right?: ReactNode;
31
+ defaultValue?: DocumentResult[];
33
32
  /**
34
33
  * To Control the value
35
34
  */
36
- value?: DocumentResult | DocumentResult[];
35
+ value?: DocumentResult[];
37
36
  /**
38
37
  * The Callback function to return the selected files as an array or object
39
38
  */
40
- onChange?: (result: DocumentResult | DocumentResult[]) => any;
41
- /**
42
- * To replace the default progress indicator
43
- */
44
- progressIndicator?: ReactNode;
39
+ onChange?: (result: DocumentResult[] | undefined) => any;
45
40
  };
46
41
 
47
- const FilePicker = (
48
- {
49
- loading,
50
- right: rightProp,
51
- progressIndicator,
52
- type,
53
- multiple,
54
- transitionStyle,
55
- mode,
56
- presentationStyle,
57
- value,
58
- onChange = () => {},
59
- onCancel,
60
- onError,
61
- ...rest
62
- }: Props,
63
- ref: any,
64
- ) => {
65
- const [displayText, setDisplayText] = useState('');
42
+ const FilePicker = ({
43
+ ref,
44
+ type,
45
+ multiple,
46
+ transitionStyle,
47
+ mode,
48
+ presentationStyle,
49
+ value: valueProp,
50
+ defaultValue,
51
+ onChange,
52
+ onCancel,
53
+ onError,
54
+ children,
55
+ ...rest
56
+ }: Props) => {
57
+ const [value, onValueChange] = useControlledValue<DocumentResult[] | undefined>({
58
+ value: valueProp,
59
+ defaultValue,
60
+ onChange,
61
+ });
66
62
 
67
63
  const { openFilePicker } = useFilePicker({
68
64
  type,
@@ -74,60 +70,34 @@ const FilePicker = (
74
70
  onError,
75
71
  });
76
72
 
77
- const onSetInputValue = useCallback(
78
- (response: DocumentResult | DocumentResult[] | undefined) => {
79
- if (Array.isArray(response)) {
80
- if (response.length > 1) {
81
- setDisplayText(`${response.length} file${response.length > 1 ? 's' : ''}`);
82
- return;
83
- }
73
+ const displayText = useMemo(() => {
74
+ if (!value) return '';
84
75
 
85
- setDisplayText(response[0].name || '');
86
- return;
87
- }
88
-
89
- setDisplayText(response?.name || '');
90
- },
91
- [],
92
- );
76
+ if (value.length > 1) {
77
+ return `${value.length} files`;
78
+ }
79
+ return value[0]?.name || '';
80
+ }, [value]);
93
81
 
94
82
  const onPress = useCallback(() => {
95
83
  openFilePicker(response => {
96
- onSetInputValue(response);
97
-
98
- onChange?.(response);
84
+ onValueChange(response);
99
85
  });
100
- }, [onChange, onSetInputValue, openFilePicker]);
101
-
102
- const rightElement = useMemo(() => {
103
- if (!loading) {
104
- return (
105
- <>
106
- {rightProp || (
107
- <IconButton type="material-community" name="upload" onPress={onPress} />
108
- )}
109
- </>
110
- );
111
- } else {
112
- return <>{progressIndicator || <ActivityIndicator />}</>;
113
- }
114
- }, [loading, onPress, progressIndicator, rightProp]);
86
+ }, [onValueChange, openFilePicker]);
115
87
 
116
- // if the value changes, we only want file name or the length of the array to display the text
117
- useEffect(() => {
118
- onSetInputValue(value);
119
- }, [onSetInputValue, value]);
88
+ const contextValue = useMemo(() => ({ onPressTrigger: onPress }), [onPress]);
120
89
 
121
90
  return (
122
- <TextInput
123
- label="Choose file"
124
- {...rest}
125
- value={displayText}
126
- editable={false}
127
- right={rightElement}
128
- ref={ref}
129
- />
91
+ <FilePickerContext value={contextValue}>
92
+ <TextInput value={displayText} {...rest} editable={false} ref={ref}>
93
+ <TextInput.Label>Choose file</TextInput.Label>
94
+ <TextInput.Right>
95
+ <IconButton type="material-community" name="upload" onPress={onPress} />
96
+ </TextInput.Right>
97
+ {children}
98
+ </TextInput>
99
+ </FilePickerContext>
130
100
  );
131
101
  };
132
102
 
133
- export default memo(forwardRef(FilePicker));
103
+ export default memo(FilePicker);
@@ -4,4 +4,5 @@ import FilePickerDefault from './FilePicker';
4
4
  export const FilePicker = getRegisteredComponentWithFallback('FilePicker', FilePickerDefault);
5
5
 
6
6
  export type { Props as FilePickerProps } from './FilePicker';
7
- export { defaultStyles } from './utils';
7
+ export type { FilePickerContextType } from './utils';
8
+ export { defaultStyles, FilePickerContext } from './utils';
@@ -1,7 +1,16 @@
1
+ import { createContext } from 'react';
1
2
  import { StyleSheet } from 'react-native-unistyles';
2
3
 
3
4
  import { getRegisteredComponentStylesWithFallback } from '../../core';
4
5
 
6
+ export type FilePickerContextType = {
7
+ onPressTrigger: () => void;
8
+ };
9
+
10
+ export const FilePickerContext = createContext<FilePickerContextType>({
11
+ onPressTrigger: () => {},
12
+ });
13
+
5
14
  const filePickerStylesDefault = StyleSheet.create({
6
15
  root: {},
7
16
  });
@@ -24,41 +24,6 @@ export type Props = TextProps & {
24
24
  testID?: string;
25
25
  };
26
26
 
27
- /**
28
- * Helper text is used in conjuction with input elements to provide additional hints for the user.
29
- *
30
- * <div class="screenshots">
31
- * <img class="small" src="screenshots/helper-text.gif" />
32
- * </div>
33
- *
34
- * ## Usage
35
- * ```js
36
- * import * as React from 'react';
37
- * import { View } from 'react-native';
38
- * import { HelperText, TextInput } from 'react-native-paper';
39
- *
40
- * const MyComponent = () => {
41
- * const [text, setText] = React.useState('');
42
- *
43
- * const onChangeText = text => setText(text);
44
- *
45
- * const hasErrors = () => {
46
- * return !text.includes('@');
47
- * };
48
- *
49
- * return (
50
- * <View>
51
- * <TextInput label="Email" value={text} onChangeText={onChangeText} />
52
- * <HelperText variant="error" visible={hasErrors()}>
53
- * Email address is invalid!
54
- * </HelperText>
55
- * </View>
56
- * );
57
- * };
58
- *
59
- * export default MyComponent;
60
- * ```
61
- */
62
27
  const HelperText = ({
63
28
  style: styleProp,
64
29
  variant = 'info',
@@ -1,6 +1,7 @@
1
1
  // import { textFactory } from '../Text/textFactory';
2
- import { memoize } from '../../utils/lodash';
3
- import { IconPacks, type IconType } from './types';
2
+ import { memoize } from '@react-native-molecules/utils/helpers/lodash';
3
+
4
+ import type { IconType } from './types';
4
5
 
5
6
  const customIcons: any = {};
6
7
 
@@ -10,9 +11,9 @@ export const registerCustomIconType = (id: string, customIcon: any) => {
10
11
 
11
12
  export default memoize((type: IconType) => {
12
13
  switch (type) {
13
- case IconPacks.MaterialCommunity:
14
+ case 'material-community':
14
15
  return require('@react-native-vector-icons/material-design-icons').default;
15
- case IconPacks.Feather:
16
+ case 'feather':
16
17
  return require('@react-native-vector-icons/feather').default;
17
18
  default:
18
19
  if (Object.prototype.hasOwnProperty.call(customIcons, type)) {
@@ -4,4 +4,4 @@ import IconDefault from './Icon';
4
4
  export const Icon = getRegisteredComponentWithFallback('Icon', IconDefault);
5
5
 
6
6
  export { registerCustomIconType } from './iconFactory';
7
- export type { IconPacks, IconProps, IconType } from './types';
7
+ export type { CustomIconTypes, IconProps, IconType } from './types';
@@ -1,10 +1,5 @@
1
1
  import type { ColorValue, TextProps } from 'react-native';
2
2
 
3
- export enum IconPacks {
4
- MaterialCommunity = 'material-community',
5
- Feather = 'feather',
6
- }
7
-
8
3
  export interface VectorIconProps extends TextProps {
9
4
  /**
10
5
  * Size of the icon, can also be passed as fontSize in the style object.
@@ -28,7 +23,23 @@ export interface VectorIconProps extends TextProps {
28
23
  color?: ColorValue | number | undefined;
29
24
  }
30
25
 
31
- export type IconType = `${IconPacks}`; // in TS 4.1+, we can do this to make enum values as a union type
26
+ /**
27
+ * Interface for registering custom icon types.
28
+ * Users can extend this interface via declaration merging to add their own icon types.
29
+ *
30
+ * @example
31
+ * // In your app's type declarations (e.g., global.d.ts or a dedicated types file)
32
+ * declare module 'react-native-molecules' {
33
+ * interface CustomIconTypes {
34
+ * 'my-custom-icons': true;
35
+ * 'another-icon-set': true;
36
+ * }
37
+ * }
38
+ */
39
+
40
+ export interface CustomIconTypes {}
41
+
42
+ export type IconType = 'material-community' | 'feather' | keyof CustomIconTypes;
32
43
 
33
44
  export type IconProps = VectorIconProps & {
34
45
  type?: IconType;
@@ -1,24 +1,24 @@
1
- import color from 'color';
2
1
  import { forwardRef, memo, useMemo } from 'react';
3
2
  import {
4
3
  type GestureResponderEvent,
5
4
  type StyleProp,
6
5
  type TextStyle,
7
6
  type ViewProps,
8
- type ViewStyle,
9
7
  } from 'react-native';
10
8
 
11
- import { useActionState } from '../../hooks/useActionState';
9
+ import { useActionState } from '../../hooks';
12
10
  import { resolveStateVariant } from '../../utils';
13
- import { Icon, type IconType } from '../Icon';
11
+ import { Icon, type IconProps, type IconType } from '../Icon';
14
12
  import CrossFadeIcon from '../Icon/CrossFadeIcon';
15
13
  import { StateLayer } from '../StateLayer';
16
- import { Surface } from '../Surface';
17
14
  import { TouchableRipple, type TouchableRippleProps } from '../TouchableRipple';
18
- import type { IconButtonVariant } from './types';
19
- import { defaultStyles, iconButtonSizeToIconSizeMap } from './utils';
20
-
21
- const whiteSpace = 12;
15
+ import type { IconButtonShape, IconButtonVariant, IconButtonWidth } from './types';
16
+ import {
17
+ defaultStyles,
18
+ iconButtonConstants,
19
+ iconButtonDefaultProps,
20
+ iconButtonSizeToIconSizeMap,
21
+ } from './utils';
22
22
 
23
23
  export type Props = Omit<TouchableRippleProps, 'children' | 'style'> & {
24
24
  /**
@@ -29,6 +29,14 @@ export type Props = Omit<TouchableRippleProps, 'children' | 'style'> & {
29
29
  * Mode of the icon button. By default there is no specified mode - only pressable icon will be rendered.
30
30
  */
31
31
  variant?: IconButtonVariant;
32
+ /**
33
+ * Container shape. Material 3 supports round and square icon buttons.
34
+ */
35
+ shape?: IconButtonShape;
36
+ /**
37
+ * Container width option. `default` maps to Material 3's uniform width.
38
+ */
39
+ width?: IconButtonWidth;
32
40
  /**
33
41
  * Whether icon button is selected. A selected button receives alternative combination of icon and container colors.
34
42
  */
@@ -63,14 +71,11 @@ export type Props = Omit<TouchableRippleProps, 'children' | 'style'> & {
63
71
  */
64
72
  style?: StyleProp<TextStyle>;
65
73
  iconStyle?: StyleProp<TextStyle>;
74
+ iconProps?: Omit<IconProps, 'name' | 'type' | 'style' | 'color' | 'size'>;
66
75
  /**
67
76
  * color of the icon
68
77
  */
69
78
  color?: string;
70
- /**
71
- * Style of the innerContainer
72
- */
73
- innerContainerStyle?: ViewStyle;
74
79
  /**
75
80
  * Props for the state layer
76
81
  * */
@@ -82,20 +87,22 @@ const emptyObject = {};
82
87
  const IconButton = (
83
88
  {
84
89
  name,
85
- size = 24,
90
+ size = iconButtonDefaultProps.size,
86
91
  color: _iconColor,
87
92
  type,
88
93
  accessibilityLabel,
89
94
  disabled = false,
90
95
  onPress,
91
96
  selected = false,
92
- animated = false,
93
- variant = 'default',
97
+ animated = iconButtonDefaultProps.animated,
98
+ variant = iconButtonDefaultProps.variant,
99
+ shape = iconButtonDefaultProps.shape,
100
+ width = iconButtonDefaultProps.width,
94
101
  style,
95
- innerContainerStyle: innerContainerStyleProp = emptyObject,
96
102
  testID,
97
103
  stateLayerProps = emptyObject,
98
- iconStyle,
104
+ iconStyle: iconStyleProp,
105
+ iconProps,
99
106
  ...rest
100
107
  }: Props,
101
108
  ref: any,
@@ -117,100 +124,93 @@ const IconButton = (
117
124
  variant: variant as any,
118
125
  // @ts-ignore // TODO - fix this
119
126
  state,
127
+ // @ts-ignore // TODO - fix this
120
128
  size: typeof size === 'string' && size ? size : undefined,
121
129
  });
122
130
 
123
131
  const {
124
132
  iconColor,
125
133
  iconSize,
126
- rippleColor,
127
134
  containerStyle,
128
135
  accessibilityState,
129
- innerContainerStyle,
130
136
  // accessibilityTraits,
131
137
  stateLayerStyle,
138
+ iconStyle,
132
139
  } = useMemo(() => {
133
140
  const iconSizeInNum =
134
141
  iconButtonSizeToIconSizeMap[size as keyof typeof iconButtonSizeToIconSizeMap] ??
135
- (typeof size === 'number' && size ? (size as number) : undefined);
136
-
137
- let _rippleColor: string | undefined;
138
-
139
- try {
140
- _rippleColor = color(_iconColor).alpha(0.12).rgb().string();
141
- } catch (e) {
142
- _rippleColor = undefined;
143
- }
142
+ (typeof size === 'number' && size ? (size as number) : 24);
143
+ const containerHeight = Math.max(
144
+ iconButtonConstants.minContainerSize,
145
+ iconSizeInNum + iconButtonConstants.containerPadding,
146
+ );
147
+ const widthAdjustment =
148
+ width === 'narrow'
149
+ ? iconButtonConstants.narrowWidthAdjustment
150
+ : width === 'wide'
151
+ ? iconButtonConstants.wideWidthAdjustment
152
+ : 0;
153
+ const containerWidth = Math.max(iconSizeInNum, containerHeight + widthAdjustment);
154
+ const borderRadius =
155
+ shape === 'round' ? containerHeight / 2 : iconButtonConstants.squareCornerRadius;
144
156
 
145
157
  return {
146
158
  iconColor: _iconColor,
147
159
  iconSize: iconSizeInNum,
148
- rippleColor: _rippleColor,
149
- innerContainerStyle: [defaultStyles.innerContainer, innerContainerStyleProp],
150
160
  containerStyle: [
151
- iconSizeInNum
152
- ? {
153
- width: iconSizeInNum + whiteSpace,
154
- height: iconSizeInNum + whiteSpace,
155
- }
156
- : {},
157
161
  defaultStyles.root,
162
+ {
163
+ width: containerWidth,
164
+ height: containerHeight,
165
+ borderRadius,
166
+ },
158
167
  style,
159
168
  ],
169
+ iconStyle: [defaultStyles.icon, iconStyleProp],
160
170
  // accessibilityTraits: disabled ? ['button', 'disabled'] : 'button',
161
171
  accessibilityState: { disabled },
162
- stateLayerStyle: [defaultStyles.stateLayer, stateLayerProps?.style],
172
+ stateLayerStyle: [defaultStyles.stateLayer, { borderRadius }, stateLayerProps?.style],
163
173
  };
164
174
  // eslint-disable-next-line react-hooks/exhaustive-deps
165
- }, [
166
- _iconColor,
167
- disabled,
168
- innerContainerStyleProp,
169
- size,
170
- stateLayerProps?.style,
171
- style,
172
- state,
173
- variant,
174
- ]);
175
+ }, [_iconColor, disabled, shape, size, stateLayerProps?.style, style, state, variant, width]);
175
176
 
176
177
  return (
177
- <Surface style={containerStyle} elevation={0}>
178
- <TouchableRipple
179
- borderless
180
- centered
181
- onPress={onPress}
182
- rippleColor={rippleColor}
183
- accessibilityLabel={accessibilityLabel}
184
- style={innerContainerStyle}
185
- // accessibilityTraits={accessibilityTraits}
186
- // accessibilityComponentType="button"
187
- accessibilityRole="button"
188
- accessibilityState={accessibilityState}
189
- disabled={disabled}
190
- hitSlop={
191
- // @ts-ignore
192
- TouchableRipple?.supported ? rippleSupportedHitSlop : rippleUnsupportedHitSlop
193
- }
178
+ <TouchableRipple
179
+ borderless
180
+ centered={shape === 'round' && width === 'default'}
181
+ onPress={onPress}
182
+ rippleAlpha={0.12}
183
+ accessibilityLabel={accessibilityLabel}
184
+ style={containerStyle}
185
+ // accessibilityTraits={accessibilityTraits}
186
+ // accessibilityComponentType="button"
187
+ accessibilityRole="button"
188
+ accessibilityState={accessibilityState}
189
+ disabled={disabled}
190
+ hitSlop={
194
191
  // @ts-ignore
195
- ref={actionsRef}
196
- testID={testID}
197
- {...rest}>
198
- <>
199
- <IconComponent
200
- color={iconColor}
201
- name={name}
202
- size={iconSize}
203
- type={type}
204
- style={iconStyle}
205
- />
206
- <StateLayer
207
- testID={testID ? `${testID}-stateLayer` : ''}
208
- {...stateLayerProps}
209
- style={stateLayerStyle}
210
- />
211
- </>
212
- </TouchableRipple>
213
- </Surface>
192
+ TouchableRipple?.supported ? rippleSupportedHitSlop : rippleUnsupportedHitSlop
193
+ }
194
+ // @ts-ignore
195
+ ref={actionsRef}
196
+ testID={testID}
197
+ {...rest}>
198
+ <>
199
+ <IconComponent
200
+ color={iconColor}
201
+ name={name}
202
+ size={iconSize}
203
+ type={type}
204
+ style={iconStyle}
205
+ {...iconProps}
206
+ />
207
+ <StateLayer
208
+ testID={testID ? `${testID}-stateLayer` : ''}
209
+ {...stateLayerProps}
210
+ style={stateLayerStyle}
211
+ />
212
+ </>
213
+ </TouchableRipple>
214
214
  );
215
215
  };
216
216
 
@@ -4,4 +4,5 @@ import IconButtonDefault from './IconButton';
4
4
  export const IconButton = getRegisteredComponentWithFallback('IconButton', IconButtonDefault);
5
5
 
6
6
  export type { Props as IconButtonProps } from './IconButton';
7
+ export type * from './types';
7
8
  export { defaultStyles } from './utils';
@@ -1 +1,11 @@
1
1
  export type IconButtonVariant = 'default' | 'outlined' | 'contained' | 'contained-tonal';
2
+ export type IconButtonShape = 'round' | 'square';
3
+ export type IconButtonWidth = 'narrow' | 'default' | 'wide';
4
+
5
+ export type IconButtonDefaultProps = {
6
+ size: 'xs' | 'sm' | 'md' | 'lg' | number;
7
+ variant: IconButtonVariant;
8
+ shape: IconButtonShape;
9
+ width: IconButtonWidth;
10
+ animated: boolean;
11
+ };