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

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 +244 -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 +20 -20
  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
@@ -0,0 +1,81 @@
1
+ import { createFastContext } from '@react-native-molecules/utils/fast-context';
2
+ import type { View } from 'react-native';
3
+
4
+ import {
5
+ ListContext,
6
+ ListContextProvider,
7
+ useListContext,
8
+ useListContextValue,
9
+ useListStoreRef,
10
+ } from '../List';
11
+ import { registerPortalContext } from '../Portal';
12
+ import type { DefaultItemT, SelectDropdownContextValue, SelectSearchContextValue } from './types';
13
+
14
+ export {
15
+ ListContext as SelectContext,
16
+ ListContextProvider as SelectContextProvider,
17
+ useListContext as useSelectContext,
18
+ useListContextValue as useSelectContextValue,
19
+ useListStoreRef as useSelectStoreRef,
20
+ };
21
+
22
+ // SelectDropdownContext - holds isOpen, onClose, triggerRef with fast-context
23
+ export type SelectDropdownContextType = SelectDropdownContextValue & {
24
+ triggerRef: React.RefObject<View> | null;
25
+ triggerLayout: { width: number; height: number } | null;
26
+ setTriggerLayout: (layout: { width: number; height: number }) => void;
27
+ triggerHovered?: boolean;
28
+ };
29
+
30
+ const selectDropdownContextDefaultValue: SelectDropdownContextType = {
31
+ isOpen: false,
32
+ onClose: () => {},
33
+ onOpen: () => {},
34
+ triggerRef: null,
35
+ triggerLayout: null,
36
+ setTriggerLayout: () => {},
37
+ triggerHovered: false,
38
+ };
39
+
40
+ const {
41
+ useStoreRef: useSelectDropdownStoreRef,
42
+ Provider: SelectDropdownContextProvider,
43
+ useContext: useSelectDropdownContext,
44
+ useContextValue: useSelectDropdownContextValue,
45
+ Context: SelectDropdownContext,
46
+ } = createFastContext<SelectDropdownContextType>(selectDropdownContextDefaultValue, true);
47
+
48
+ const selectSearchContextDefaultValue: SelectSearchContextValue<DefaultItemT> = {
49
+ searchQuery: '',
50
+ setSearchQuery: () => {},
51
+ allOptions: [],
52
+ options: [],
53
+ optionById: new Map(),
54
+ getOptionId: item => item.id,
55
+ };
56
+
57
+ const {
58
+ useStoreRef: useSelectSearchStoreRef,
59
+ Provider: SelectSearchContextProvider,
60
+ useContext: useSelectSearchContext,
61
+ useContextValue: useSelectSearchContextValue,
62
+ Context: SelectSearchContext,
63
+ } = createFastContext<SelectSearchContextValue<DefaultItemT>>(
64
+ selectSearchContextDefaultValue,
65
+ true,
66
+ );
67
+
68
+ export {
69
+ SelectDropdownContext,
70
+ SelectDropdownContextProvider,
71
+ SelectSearchContext,
72
+ SelectSearchContextProvider,
73
+ useSelectDropdownContext,
74
+ useSelectDropdownContextValue,
75
+ useSelectDropdownStoreRef,
76
+ useSelectSearchContext,
77
+ useSelectSearchContextValue,
78
+ useSelectSearchStoreRef,
79
+ };
80
+
81
+ registerPortalContext([SelectDropdownContext, SelectSearchContext]);
@@ -1,14 +1,26 @@
1
- export { default as Select } from './Select';
2
- export type {
3
- DefaultItemT,
4
- SelectContentProps,
5
- SelectContextValue,
6
- SelectDropdownContextValue,
7
- SelectDropdownProps,
8
- SelectGroupProps,
9
- SelectOptionProps,
10
- SelectProviderProps,
11
- SelectSearchInputProps,
12
- SelectTriggerProps,
13
- SelectValueProps,
14
- } from './types';
1
+ import { getRegisteredComponentWithFallback } from '../../core';
2
+ import SelectRoot, {
3
+ SelectContent,
4
+ SelectDropdown,
5
+ SelectOption,
6
+ SelectSearchInput,
7
+ SelectTrigger,
8
+ SelectTriggerOutline,
9
+ SelectValue,
10
+ } from './Select';
11
+
12
+ const SelectWithSubcomponents = Object.assign(SelectRoot, {
13
+ Trigger: SelectTrigger,
14
+ TriggerOutline: SelectTriggerOutline,
15
+ Value: SelectValue,
16
+ Dropdown: SelectDropdown,
17
+ Content: SelectContent,
18
+ Option: SelectOption,
19
+ SearchInput: SelectSearchInput,
20
+ });
21
+
22
+ export const Select = getRegisteredComponentWithFallback('Select', SelectWithSubcomponents);
23
+
24
+ export * from './context';
25
+ export type * from './types';
26
+ export * from './utils';
@@ -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 SelectProviderProps<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
@@ -62,9 +82,12 @@ export type SelectTriggerProps = ViewProps & {
62
82
  children?: ReactNode;
63
83
  };
64
84
 
85
+ export type SelectTriggerOutlineProps = ViewProps;
86
+
65
87
  // Select.Value props
66
88
  export type SelectValueProps = ViewProps & {
67
89
  placeholder?: string;
90
+ labelKey?: string;
68
91
  renderValue?: (value: DefaultItemT | DefaultItemT[] | null) => ReactNode;
69
92
  };
70
93
 
@@ -78,38 +101,22 @@ export type SelectDropdownProps = Omit<
78
101
  wrapperComponentProps?: Record<string, any>;
79
102
  };
80
103
 
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
104
  // Select.Option props
98
- export type SelectOptionProps<Option extends DefaultItemT = DefaultItemT> = ViewProps & {
105
+ // `accessibilityRole` and `role` are intentionally omitted: Select.Option forces them to
106
+ // "option" on web so the dropdown's keyboard navigator (which queries [role="option"]) can
107
+ // always find the rows. Allowing callers to override would silently break keyboard nav.
108
+ export type SelectOptionProps<Option extends DefaultItemT = DefaultItemT> = Omit<
109
+ ViewProps,
110
+ 'accessibilityRole' | 'role'
111
+ > & {
99
112
  /**
100
113
  * Unique id for the option
101
114
  */
102
115
  value: Option['id'];
103
- children?: ReactNode;
104
- renderItem?: (item: Option, isSelected: boolean) => ReactNode;
116
+ children: ReactNode;
105
117
  onPress?: (item: Option, event: GestureResponderEvent) => void;
106
118
  /**
107
119
  * When true, the option can't be selected (similar to HTML option disabled)
108
120
  */
109
121
  disabled?: boolean;
110
122
  };
111
-
112
- // Select.SearchInput props
113
- export type SelectSearchInputProps = TextInputProps & {
114
- onQueryChange?: (query: string) => void;
115
- };
@@ -0,0 +1,126 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+
3
+ import { getRegisteredComponentStylesWithFallback } from '../../core';
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
+
20
+ const triggerDefaultStyles = StyleSheet.create(theme => ({
21
+ trigger: {
22
+ borderRadius: theme.shapes.corner.extraSmall,
23
+ paddingHorizontal: theme.spacings['3'],
24
+ paddingVertical: theme.spacings['2'],
25
+ minHeight: 48,
26
+ flexDirection: 'row',
27
+ alignItems: 'center',
28
+ justifyContent: 'space-between',
29
+ width: '100%',
30
+ variants: {
31
+ state: {
32
+ disabled: {
33
+ opacity: 0.38,
34
+ backgroundColor: theme.colors.surfaceVariant,
35
+ },
36
+ errorDisabled: {
37
+ opacity: 0.38,
38
+ },
39
+ },
40
+ },
41
+ },
42
+ triggerIcon: {
43
+ marginLeft: theme.spacings['2'],
44
+ color: theme.colors.onSurfaceVariant,
45
+ },
46
+ }));
47
+
48
+ const outlineDefaultStyles = StyleSheet.create(theme => ({
49
+ outline: {
50
+ position: 'absolute',
51
+ top: 0,
52
+ left: 0,
53
+ right: 0,
54
+ bottom: 0,
55
+ borderRadius: theme.shapes.corner.extraSmall,
56
+ borderWidth: 1,
57
+ borderColor: theme.colors.outline,
58
+ pointerEvents: 'none',
59
+ variants: {
60
+ state: {
61
+ focused: {
62
+ borderWidth: 2,
63
+ borderColor: theme.colors.primary,
64
+ },
65
+ hovered: {
66
+ borderColor: theme.colors.onSurface,
67
+ },
68
+ hoveredAndFocused: {
69
+ borderWidth: 2,
70
+ borderColor: theme.colors.primary,
71
+ },
72
+ disabled: {
73
+ borderColor: theme.colors.onSurface,
74
+ },
75
+ error: {
76
+ borderColor: theme.colors.error,
77
+ },
78
+ errorFocused: {
79
+ borderWidth: 2,
80
+ borderColor: theme.colors.error,
81
+ },
82
+ errorHovered: {
83
+ borderColor: theme.colors.onErrorContainer,
84
+ },
85
+ errorFocusedAndHovered: {
86
+ borderWidth: 2,
87
+ borderColor: theme.colors.error,
88
+ },
89
+ errorDisabled: {
90
+ borderColor: theme.colors.error,
91
+ },
92
+ },
93
+ },
94
+ },
95
+ }));
96
+
97
+ export const defaultStyles = StyleSheet.create(theme => ({
98
+ valueText: {
99
+ flex: 1,
100
+ },
101
+ chipContainer: {
102
+ flexDirection: 'row',
103
+ flexWrap: 'wrap',
104
+ gap: 6,
105
+ maxWidth: '90%',
106
+ flex: 1,
107
+ },
108
+ searchInput: {
109
+ marginHorizontal: theme.spacings['2'],
110
+ marginVertical: theme.spacings['3'],
111
+ },
112
+ searchInputInput: {
113
+ height: 42,
114
+ },
115
+ }));
116
+
117
+ export const triggerStyles = getRegisteredComponentStylesWithFallback(
118
+ 'Select_Trigger',
119
+ triggerDefaultStyles,
120
+ );
121
+ export const selectOutlineStyles = getRegisteredComponentStylesWithFallback(
122
+ 'SelectOutline',
123
+ outlineDefaultStyles,
124
+ );
125
+
126
+ export const styles = getRegisteredComponentStylesWithFallback('Select', defaultStyles);
@@ -0,0 +1,244 @@
1
+ import { composeRefs } from '@radix-ui/react-compose-refs';
2
+ import * as React from 'react';
3
+ import { type PressableProps, type StyleProp, type ViewProps, type ViewStyle } from 'react-native';
4
+
5
+ declare module 'react' {
6
+ interface ReactElement {
7
+ $$typeof?: symbol | string;
8
+ }
9
+ }
10
+
11
+ const REACT_LAZY_TYPE = Symbol.for('react.lazy');
12
+
13
+ interface LazyReactElement extends React.ReactElement {
14
+ $$typeof: typeof REACT_LAZY_TYPE;
15
+ _payload: PromiseLike<Exclude<React.ReactNode, PromiseLike<any>>>;
16
+ }
17
+
18
+ /* -------------------------------------------------------------------------------------------------
19
+ * Slot
20
+ * -----------------------------------------------------------------------------------------------*/
21
+
22
+ export type Usable<T> = PromiseLike<T> | React.Context<T>;
23
+ const use: typeof React.use | undefined = (React as any)[' use '.trim().toString()];
24
+
25
+ interface SlotProps
26
+ extends Omit<ViewProps, 'children'>,
27
+ Partial<
28
+ Pick<
29
+ PressableProps,
30
+ 'onPress' | 'onPressIn' | 'onPressOut' | 'onLongPress' | 'disabled'
31
+ >
32
+ > {
33
+ children?: React.ReactNode;
34
+ }
35
+
36
+ function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
37
+ return typeof value === 'object' && value !== null && 'then' in value;
38
+ }
39
+
40
+ function isLazyComponent(element: React.ReactNode): element is LazyReactElement {
41
+ return (
42
+ element != null &&
43
+ typeof element === 'object' &&
44
+ '$$typeof' in element &&
45
+ element.$$typeof === REACT_LAZY_TYPE &&
46
+ '_payload' in element &&
47
+ isPromiseLike(element._payload)
48
+ );
49
+ }
50
+
51
+ /* @__NO_SIDE_EFFECTS__ */ export function createSlot(ownerName: string) {
52
+ const SlotClone = createSlotClone(ownerName);
53
+ const Slot = React.forwardRef<any, SlotProps>((props, forwardedRef) => {
54
+ const { children: childrenProp, ...slotProps } = props;
55
+ let children = childrenProp;
56
+ if (isLazyComponent(children) && typeof use === 'function') {
57
+ children = use(children._payload);
58
+ }
59
+ const childrenArray = React.Children.toArray(children);
60
+ const slottable = childrenArray.find(isSlottable);
61
+
62
+ if (slottable) {
63
+ // the new element to render is the one passed as a child of `Slottable`
64
+ const newElement = slottable.props.children;
65
+
66
+ const newChildren = childrenArray.map(child => {
67
+ if (child === slottable) {
68
+ // because the new element will be the one rendered, we are only interested
69
+ // in grabbing its children (`newElement.props.children`)
70
+ if (React.Children.count(newElement) > 1) return React.Children.only(null);
71
+ return React.isValidElement(newElement)
72
+ ? (newElement.props as { children: React.ReactNode }).children
73
+ : null;
74
+ } else {
75
+ return child;
76
+ }
77
+ });
78
+
79
+ return (
80
+ <SlotClone {...slotProps} ref={forwardedRef}>
81
+ {React.isValidElement(newElement)
82
+ ? React.cloneElement(newElement, undefined, newChildren)
83
+ : null}
84
+ </SlotClone>
85
+ );
86
+ }
87
+
88
+ return (
89
+ <SlotClone {...slotProps} ref={forwardedRef}>
90
+ {children}
91
+ </SlotClone>
92
+ );
93
+ });
94
+
95
+ Slot.displayName = `${ownerName}.Slot`;
96
+ return Slot;
97
+ }
98
+
99
+ const Slot = createSlot('Slot');
100
+
101
+ /* -------------------------------------------------------------------------------------------------
102
+ * SlotClone
103
+ * -----------------------------------------------------------------------------------------------*/
104
+
105
+ interface SlotCloneProps {
106
+ children: React.ReactNode;
107
+ }
108
+
109
+ /* @__NO_SIDE_EFFECTS__ */ function createSlotClone(ownerName: string) {
110
+ const SlotClone = React.forwardRef<any, SlotCloneProps>((props, forwardedRef) => {
111
+ const { children: childrenProp, ...slotProps } = props;
112
+ let children = childrenProp;
113
+ if (isLazyComponent(children) && typeof use === 'function') {
114
+ children = use(children._payload);
115
+ }
116
+
117
+ if (React.isValidElement(children)) {
118
+ const childrenRef = getElementRef(children);
119
+ const mergedProps = mergeProps(slotProps, children.props as AnyProps);
120
+ // do not pass ref to React.Fragment for React 19 compatibility
121
+ if (children.type !== React.Fragment) {
122
+ mergedProps.ref = forwardedRef
123
+ ? composeRefs(forwardedRef, childrenRef)
124
+ : childrenRef;
125
+ }
126
+ return React.cloneElement(children, mergedProps);
127
+ }
128
+
129
+ return React.Children.count(children) > 1 ? React.Children.only(null) : null;
130
+ });
131
+
132
+ SlotClone.displayName = `${ownerName}.SlotClone`;
133
+ return SlotClone;
134
+ }
135
+
136
+ /* -------------------------------------------------------------------------------------------------
137
+ * Slottable
138
+ * -----------------------------------------------------------------------------------------------*/
139
+
140
+ const SLOTTABLE_IDENTIFIER = Symbol('radix.slottable');
141
+
142
+ interface SlottableProps {
143
+ children: React.ReactNode;
144
+ }
145
+
146
+ export interface SlottableComponent extends React.FC<SlottableProps> {
147
+ __radixId: symbol;
148
+ }
149
+
150
+ /* @__NO_SIDE_EFFECTS__ */ export function createSlottable(ownerName: string) {
151
+ const Slottable: SlottableComponent = ({ children }) => {
152
+ return <>{children}</>;
153
+ };
154
+ Slottable.displayName = `${ownerName}.Slottable`;
155
+ Slottable.__radixId = SLOTTABLE_IDENTIFIER;
156
+ return Slottable;
157
+ }
158
+
159
+ const Slottable = createSlottable('Slottable');
160
+
161
+ /* ---------------------------------------------------------------------------------------------- */
162
+
163
+ type AnyProps = Record<string, any>;
164
+
165
+ function isSlottable(
166
+ child: React.ReactNode,
167
+ ): child is React.ReactElement<SlottableProps, typeof Slottable> {
168
+ return (
169
+ React.isValidElement(child) &&
170
+ typeof child.type === 'function' &&
171
+ '__radixId' in child.type &&
172
+ child.type.__radixId === SLOTTABLE_IDENTIFIER
173
+ );
174
+ }
175
+
176
+ function mergeProps(slotProps: AnyProps, childProps: AnyProps) {
177
+ // all child props should override
178
+ const overrideProps = { ...childProps };
179
+
180
+ for (const propName in childProps) {
181
+ const slotPropValue = slotProps[propName];
182
+ const childPropValue = childProps[propName];
183
+
184
+ const isHandler = /^on[A-Z]/.test(propName);
185
+ if (isHandler) {
186
+ // if the handler exists on both, we compose them
187
+ if (slotPropValue && childPropValue) {
188
+ overrideProps[propName] = (...args: unknown[]) => {
189
+ const result = childPropValue(...args);
190
+ slotPropValue(...args);
191
+ return result;
192
+ };
193
+ }
194
+ // but if it exists only on the slot, we use only this one
195
+ else if (slotPropValue) {
196
+ overrideProps[propName] = slotPropValue;
197
+ }
198
+ }
199
+ // if it's `style`, we merge them (React Native styles can be arrays)
200
+ else if (propName === 'style') {
201
+ const slotStyle = slotPropValue as StyleProp<ViewStyle> | undefined;
202
+ const childStyle = childPropValue as StyleProp<ViewStyle> | undefined;
203
+ if (slotStyle || childStyle) {
204
+ overrideProps[propName] = [slotStyle, childStyle].filter(
205
+ Boolean,
206
+ ) as StyleProp<ViewStyle>;
207
+ }
208
+ }
209
+ }
210
+
211
+ return { ...slotProps, ...overrideProps };
212
+ }
213
+
214
+ // Before React 19 accessing `element.props.ref` will throw a warning and suggest using `element.ref`
215
+ // After React 19 accessing `element.ref` does the opposite.
216
+ // https://github.com/facebook/react/pull/28348
217
+ //
218
+ // Access the ref using the method that doesn't yield a warning.
219
+ function getElementRef(element: React.ReactElement) {
220
+ // React <=18 in DEV
221
+ let getter = Object.getOwnPropertyDescriptor(element.props, 'ref')?.get;
222
+ let mayWarn = getter && 'isReactWarning' in getter && getter.isReactWarning;
223
+ if (mayWarn) {
224
+ return (element as any).ref;
225
+ }
226
+
227
+ // React 19 in DEV
228
+ getter = Object.getOwnPropertyDescriptor(element, 'ref')?.get;
229
+ mayWarn = getter && 'isReactWarning' in getter && getter.isReactWarning;
230
+ if (mayWarn) {
231
+ return (element.props as { ref?: React.Ref<unknown> }).ref;
232
+ }
233
+
234
+ // Not DEV
235
+ return (element.props as { ref?: React.Ref<unknown> }).ref || (element as any).ref;
236
+ }
237
+
238
+ export {
239
+ //
240
+ Slot as Root,
241
+ Slot,
242
+ Slottable,
243
+ };
244
+ export type { SlotProps };