@expo/ui 0.0.2 → 0.1.0-alpha.0
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.
- package/BottomSheet/index.ios.tsx +34 -0
- package/BottomSheet/index.tsx +12 -0
- package/{components/Button → Button}/index.tsx +47 -16
- package/Button/types.ts +54 -0
- package/CHANGELOG.md +40 -0
- package/ColorPicker/index.tsx +54 -0
- package/ContextMenu/index.android.tsx +72 -0
- package/{components/ContextMenu → ContextMenu}/index.tsx +80 -30
- package/{components/ContextMenu → ContextMenu}/utils.ts +4 -0
- package/DatePicker/index.tsx +97 -0
- package/Gauge/index.tsx +73 -0
- package/Label/index.ios.tsx +16 -0
- package/Label/index.tsx +34 -0
- package/List/index.ios.tsx +35 -0
- package/List/index.tsx +99 -0
- package/{components/Picker → Picker}/index.tsx +32 -19
- package/Progress/index.tsx +48 -0
- package/Section/index.ios.tsx +12 -0
- package/Section/index.tsx +19 -0
- package/{components/Slider → Slider}/index.tsx +19 -10
- package/{components/Switch → Switch}/index.tsx +37 -54
- package/TextInput/index.tsx +108 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/ui/DatePickerView.kt +160 -0
- package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +10 -0
- package/android/src/main/java/expo/modules/ui/PickerView.kt +93 -31
- package/android/src/main/java/expo/modules/ui/ProgressView.kt +83 -0
- package/android/src/main/java/expo/modules/ui/SwitchView.kt +56 -39
- package/android/src/main/java/expo/modules/ui/TextInputView.kt +75 -0
- package/android/src/main/java/expo/modules/ui/Utils.kt +17 -0
- package/android/src/main/java/expo/modules/ui/button/Button.kt +33 -4
- package/android/src/main/java/expo/modules/ui/menu/ContextMenu.kt +49 -32
- package/android/src/main/java/expo/modules/ui/menu/ContextMenuRecords.kt +10 -3
- package/build/{components/Button → Button}/index.d.ts +36 -13
- package/build/Button/index.d.ts.map +1 -0
- package/build/Button/types.d.ts +5 -0
- package/build/Button/types.d.ts.map +1 -0
- package/build/{components/ContextMenu → ContextMenu}/index.d.ts +68 -19
- package/build/ContextMenu/index.d.ts.map +1 -0
- package/build/ContextMenu/utils.d.ts.map +1 -0
- package/build/Label/index.d.ts +28 -0
- package/build/Label/index.d.ts.map +1 -0
- package/build/List/index.d.ts +87 -0
- package/build/List/index.d.ts.map +1 -0
- package/build/Picker/index.d.ts +76 -0
- package/build/Picker/index.d.ts.map +1 -0
- package/build/Section/index.d.ts +16 -0
- package/build/Section/index.d.ts.map +1 -0
- package/build/{components/Slider → Slider}/index.d.ts +18 -10
- package/build/Slider/index.d.ts.map +1 -0
- package/build/{components/Switch → Switch}/index.d.ts +18 -36
- package/build/Switch/index.d.ts.map +1 -0
- package/build/src/types.d.ts +15 -0
- package/build/src/types.d.ts.map +1 -0
- package/expo-module.config.json +3 -3
- package/ios/BottomSheetView.swift +86 -0
- package/ios/Button/Button.swift +52 -44
- package/ios/Button/ButtonProps.swift +1 -0
- package/ios/ColorPickerView.swift +54 -0
- package/ios/ContextMenu/ContextMenu.swift +71 -8
- package/ios/ContextMenu/ContextMenuRecords.swift +6 -0
- package/ios/DateTimePickerView.swift +85 -0
- package/ios/ExpoUIModule.swift +10 -0
- package/ios/Gauge/Gauge.swift +49 -0
- package/ios/Gauge/GaugeProps.swift +28 -0
- package/ios/Label.swift +29 -0
- package/ios/List.swift +127 -0
- package/ios/PickerView.swift +38 -29
- package/ios/ProgressView.swift +40 -0
- package/ios/SectionView.swift +12 -5
- package/ios/SliderView.swift +6 -2
- package/ios/SwitchView.swift +6 -3
- package/ios/TextInput/TextInputView.swift +83 -0
- package/package.json +4 -6
- package/src/types.ts +18 -0
- package/tsconfig.json +1 -1
- package/build/components/Button/index.d.ts.map +0 -1
- package/build/components/ContextMenu/index.d.ts.map +0 -1
- package/build/components/ContextMenu/utils.d.ts.map +0 -1
- package/build/components/Picker/index.d.ts +0 -65
- package/build/components/Picker/index.d.ts.map +0 -1
- package/build/components/Section/index.d.ts +0 -12
- package/build/components/Section/index.d.ts.map +0 -1
- package/build/components/Section/index.ios.d.ts +0 -8
- package/build/components/Section/index.ios.d.ts.map +0 -1
- package/build/components/Slider/index.d.ts.map +0 -1
- package/build/components/Switch/index.d.ts.map +0 -1
- package/build/src/index.d.ts +0 -13
- package/build/src/index.d.ts.map +0 -1
- package/components/Section/index.ios.tsx +0 -58
- package/components/Section/index.tsx +0 -56
- package/src/index.ts +0 -22
- /package/build/{components/ContextMenu → ContextMenu}/utils.d.ts +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { requireNativeView } from 'expo';
|
|
2
|
+
import { Dimensions, NativeSyntheticEvent, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { BottomSheetProps } from '.';
|
|
5
|
+
|
|
6
|
+
type NativeBottomSheetProps = Omit<BottomSheetProps, 'onIsOpenedChange'> & {
|
|
7
|
+
onIsOpenedChange: (event: NativeSyntheticEvent<{ isOpened: boolean }>) => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const BottomSheetNativeView: React.ComponentType<NativeBottomSheetProps> = requireNativeView(
|
|
11
|
+
'ExpoUI',
|
|
12
|
+
'BottomSheetView'
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export function transformBottomSheetProps(props: BottomSheetProps): NativeBottomSheetProps {
|
|
16
|
+
return {
|
|
17
|
+
...props,
|
|
18
|
+
onIsOpenedChange: ({ nativeEvent: { isOpened } }) => {
|
|
19
|
+
props?.onIsOpenedChange?.(isOpened);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function BottomSheet(props: BottomSheetProps) {
|
|
25
|
+
const { width } = Dimensions.get('window');
|
|
26
|
+
return (
|
|
27
|
+
<View>
|
|
28
|
+
<BottomSheetNativeView
|
|
29
|
+
style={{ position: 'absolute', width }}
|
|
30
|
+
{...transformBottomSheetProps(props)}
|
|
31
|
+
/>
|
|
32
|
+
</View>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export type BottomSheetProps = {
|
|
4
|
+
style?: StyleProp<ViewStyle>;
|
|
5
|
+
children: any;
|
|
6
|
+
isOpened: boolean;
|
|
7
|
+
onIsOpenedChange: (isOpened: boolean) => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function BottomSheet({ children }: BottomSheetProps) {
|
|
11
|
+
return children;
|
|
12
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { requireNativeView } from 'expo';
|
|
2
|
-
import { StyleProp, StyleSheet, ViewStyle } from 'react-native';
|
|
2
|
+
import { Platform, StyleProp, StyleSheet, ViewStyle } from 'react-native';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { MaterialIcon } from './types';
|
|
5
|
+
import { ViewEvent } from '../src/types';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* The role of the button.
|
|
@@ -14,10 +15,12 @@ export type ButtonRole = 'default' | 'cancel' | 'destructive';
|
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* The built-in button styles available on iOS and Android.
|
|
18
|
+
*
|
|
17
19
|
* Common styles:
|
|
18
20
|
* - `default` - The default system button style.
|
|
19
21
|
* - `bordered` - A button with a light fill. On Android equivalent to `FilledTonalButton`.
|
|
20
22
|
* - `borderless` - A button with no background or border. On Android equivalent to `TextButton`.
|
|
23
|
+
*
|
|
21
24
|
* Apple-only styles:
|
|
22
25
|
* - `borderedProminent` - A bordered button with a prominent appearance.
|
|
23
26
|
* - `plain` - A button with no border or background and a less prominent text.
|
|
@@ -26,12 +29,10 @@ export type ButtonRole = 'default' | 'cancel' | 'destructive';
|
|
|
26
29
|
* - `accessoryBarAction` - A button style for accessory bar actions.
|
|
27
30
|
* - `card` - A button style for cards.
|
|
28
31
|
* - `link` - A button style for links.
|
|
32
|
+
*
|
|
29
33
|
* Android-only styles:
|
|
30
34
|
* - `outlined` - A button with an outline.
|
|
31
35
|
* - `elevated` - A filled button with a shadow.
|
|
32
|
-
*
|
|
33
|
-
* @platform android
|
|
34
|
-
* @platform ios
|
|
35
36
|
*/
|
|
36
37
|
export type ButtonVariant =
|
|
37
38
|
// Common
|
|
@@ -50,6 +51,17 @@ export type ButtonVariant =
|
|
|
50
51
|
| 'outlined'
|
|
51
52
|
| 'elevated';
|
|
52
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Colors for button's core elements.
|
|
56
|
+
* @platform android
|
|
57
|
+
*/
|
|
58
|
+
export type ButtonElementColors = {
|
|
59
|
+
containerColor?: string;
|
|
60
|
+
contentColor?: string;
|
|
61
|
+
disabledContainerColor?: string;
|
|
62
|
+
disabledContentColor?: string;
|
|
63
|
+
};
|
|
64
|
+
|
|
53
65
|
export type ButtonProps = {
|
|
54
66
|
/**
|
|
55
67
|
* A callback that is called when the button is pressed.
|
|
@@ -57,9 +69,12 @@ export type ButtonProps = {
|
|
|
57
69
|
onPress?: () => void;
|
|
58
70
|
/**
|
|
59
71
|
* A string describing the system image to display in the button.
|
|
60
|
-
*
|
|
72
|
+
* Uses Material Icons on Android and SF Symbols on iOS.
|
|
61
73
|
*/
|
|
62
|
-
systemImage?:
|
|
74
|
+
systemImage?: {
|
|
75
|
+
ios?: string;
|
|
76
|
+
android?: MaterialIcon;
|
|
77
|
+
};
|
|
63
78
|
/**
|
|
64
79
|
* Indicated the role of the button.
|
|
65
80
|
* @platform ios
|
|
@@ -81,21 +96,27 @@ export type ButtonProps = {
|
|
|
81
96
|
* Colors for button's core elements.
|
|
82
97
|
* @platform android
|
|
83
98
|
*/
|
|
84
|
-
elementColors?:
|
|
85
|
-
containerColor?: string;
|
|
86
|
-
contentColor?: string;
|
|
87
|
-
disabledContainerColor?: string;
|
|
88
|
-
disabledContentColor?: string;
|
|
89
|
-
};
|
|
99
|
+
elementColors?: ButtonElementColors;
|
|
90
100
|
/**
|
|
91
101
|
* Button color.
|
|
92
102
|
*/
|
|
93
103
|
color?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Disabled state of the button.
|
|
106
|
+
*/
|
|
107
|
+
disabled?: boolean;
|
|
94
108
|
};
|
|
95
109
|
|
|
96
|
-
|
|
110
|
+
/**
|
|
111
|
+
* @hidden
|
|
112
|
+
*/
|
|
113
|
+
export type NativeButtonProps = Omit<
|
|
114
|
+
ButtonProps,
|
|
115
|
+
'role' | 'onPress' | 'children' | 'systemImage'
|
|
116
|
+
> & {
|
|
97
117
|
buttonRole?: ButtonRole;
|
|
98
118
|
text: string;
|
|
119
|
+
systemImage?: string;
|
|
99
120
|
} & ViewEvent<'onButtonPressed', void>;
|
|
100
121
|
|
|
101
122
|
// We have to work around the `role` and `onPress` props being reserved by React Native.
|
|
@@ -104,12 +125,16 @@ const ButtonNativeView: React.ComponentType<NativeButtonProps> = requireNativeVi
|
|
|
104
125
|
'Button'
|
|
105
126
|
);
|
|
106
127
|
|
|
128
|
+
/**
|
|
129
|
+
* @hidden
|
|
130
|
+
*/
|
|
107
131
|
export function transformButtonProps(props: ButtonProps): NativeButtonProps {
|
|
108
|
-
const { role, children, onPress, ...restProps } = props;
|
|
132
|
+
const { role, children, onPress, systemImage, ...restProps } = props;
|
|
109
133
|
return {
|
|
110
134
|
...restProps,
|
|
111
135
|
text: children ?? '',
|
|
112
136
|
buttonRole: role,
|
|
137
|
+
systemImage: systemImage?.[Platform.OS as 'ios' | 'android'],
|
|
113
138
|
onButtonPressed: onPress,
|
|
114
139
|
elementColors: props.elementColors
|
|
115
140
|
? props.elementColors
|
|
@@ -121,12 +146,18 @@ export function transformButtonProps(props: ButtonProps): NativeButtonProps {
|
|
|
121
146
|
};
|
|
122
147
|
}
|
|
123
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Displays a native button component.
|
|
151
|
+
*/
|
|
124
152
|
export function Button(props: ButtonProps) {
|
|
125
153
|
// Min height from https://m3.material.io/components/buttons/specs, minWidth
|
|
126
154
|
return (
|
|
127
155
|
<ButtonNativeView
|
|
128
156
|
{...transformButtonProps(props)}
|
|
129
|
-
style={StyleSheet.compose(
|
|
157
|
+
style={StyleSheet.compose(
|
|
158
|
+
Platform.OS === 'android' ? { minWidth: 80, minHeight: 40 } : {},
|
|
159
|
+
props.style
|
|
160
|
+
)}
|
|
130
161
|
/>
|
|
131
162
|
);
|
|
132
163
|
}
|
package/Button/types.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
type Variant = 'rounded' | 'twotone' | 'outlined' | 'filled' | 'sharp';
|
|
2
|
+
|
|
3
|
+
type Icon =
|
|
4
|
+
| 'AccountBox'
|
|
5
|
+
| 'AccountCircle'
|
|
6
|
+
| 'Add'
|
|
7
|
+
| 'AddCircle'
|
|
8
|
+
| 'ArrowBack'
|
|
9
|
+
| 'ArrowDropDown'
|
|
10
|
+
| 'ArrowForward'
|
|
11
|
+
| 'Build'
|
|
12
|
+
| 'Call'
|
|
13
|
+
| 'Check'
|
|
14
|
+
| 'CheckCircle'
|
|
15
|
+
| 'Clear'
|
|
16
|
+
| 'Close'
|
|
17
|
+
| 'Create'
|
|
18
|
+
| 'DateRange'
|
|
19
|
+
| 'Delete'
|
|
20
|
+
| 'Done'
|
|
21
|
+
| 'Edit'
|
|
22
|
+
| 'Email'
|
|
23
|
+
| 'ExitToApp'
|
|
24
|
+
| 'Face'
|
|
25
|
+
| 'Favorite'
|
|
26
|
+
| 'FavoriteBorder'
|
|
27
|
+
| 'Home'
|
|
28
|
+
| 'Info'
|
|
29
|
+
| 'KeyboardArrowDown'
|
|
30
|
+
| 'KeyboardArrowLeft'
|
|
31
|
+
| 'KeyboardArrowRight'
|
|
32
|
+
| 'KeyboardArrowUp'
|
|
33
|
+
| 'List'
|
|
34
|
+
| 'LocationOn'
|
|
35
|
+
| 'Lock'
|
|
36
|
+
| 'MailOutline'
|
|
37
|
+
| 'Menu'
|
|
38
|
+
| 'MoreVert'
|
|
39
|
+
| 'Notifications'
|
|
40
|
+
| 'Person'
|
|
41
|
+
| 'Phone'
|
|
42
|
+
| 'Place'
|
|
43
|
+
| 'PlayArrow'
|
|
44
|
+
| 'Refresh'
|
|
45
|
+
| 'Search'
|
|
46
|
+
| 'Send'
|
|
47
|
+
| 'Settings'
|
|
48
|
+
| 'Share'
|
|
49
|
+
| 'ShoppingCart'
|
|
50
|
+
| 'Star'
|
|
51
|
+
| 'ThumbUp'
|
|
52
|
+
| 'Warning';
|
|
53
|
+
|
|
54
|
+
export type MaterialIcon = `${Variant}.${Icon}`;
|
package/CHANGELOG.md
CHANGED
|
@@ -10,23 +10,63 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 0.1.0-alpha.0 — 2025-04-04
|
|
14
|
+
|
|
15
|
+
### 🛠 Breaking changes
|
|
16
|
+
|
|
17
|
+
- upgrade RN to 0.78 ([#35050](https://github.com/expo/expo/pull/35050) by [@vonovak](https://github.com/vonovak))
|
|
18
|
+
|
|
19
|
+
### 🎉 New features
|
|
20
|
+
|
|
21
|
+
- [iOS] Add bottom sheet. ([#35455](https://github.com/expo/expo/pull/35455) by [@aleqsio](https://github.com/aleqsio))
|
|
22
|
+
- [iOS] Add support for `pallete` and `inline` pickers. ([#35435](https://github.com/expo/expo/pull/35435) by [@aleqsio](https://github.com/aleqsio))
|
|
23
|
+
- Add TextInput for Android. ([#35228](https://github.com/expo/expo/pull/35228) by [@aleqsio](https://github.com/aleqsio))
|
|
24
|
+
- Add context menu previews. ([#35369](https://github.com/expo/expo/pull/35369) by [@aleqsio](https://github.com/aleqsio)).
|
|
25
|
+
- Add TextInput for iOS. ([#35098](https://github.com/expo/expo/pull/35098) by [@aleqsio](https://github.com/aleqsio))
|
|
26
|
+
- Add `disabled` button prop. ([#35115](https://github.com/expo/expo/pull/35115) by [@andrew-levy](https://github.com/andrew-levy))
|
|
27
|
+
- Add Radio option to Picker Component for Android. ([#34629](https://github.com/expo/expo/pull/34629)) by [@benjaminkomen](https://github.com/benjaminkomen)
|
|
28
|
+
- Add color support for `ContextMenu` components. ([#34787](https://github.com/expo/expo/pull/34787) by [@behenate](https://github.com/behenate))
|
|
29
|
+
- Adds `DateTimePicker` component. ([#34883](https://github.com/expo/expo/pull/34883) by [@alanjhughes](https://github.com/alanjhughes))
|
|
30
|
+
- Add CircularProgress and LinearProgress component. ([#34907](https://github.com/expo/expo/pull/34907) by [@janicduplessis](https://github.com/janicduplessis))
|
|
31
|
+
- Add Gauge component ([#35032](https://github.com/expo/expo/pull/35032) by [@jakex7](https://github.com/jakex7))
|
|
32
|
+
- Add List and Label component ([#35222](https://github.com/expo/expo/pull/35222) by [@Pflaumenbaum](https://github.com/Pflaumenbaum))
|
|
33
|
+
|
|
34
|
+
### 🐛 Bug fixes
|
|
35
|
+
|
|
36
|
+
- [iOS] Fix tvOS breakage. ([#35146](https://github.com/expo/expo/pull/35146) by [@douglowder](https://github.com/douglowder))
|
|
37
|
+
|
|
38
|
+
### 💡 Others
|
|
39
|
+
|
|
40
|
+
- Refactor imports, update docs ([#35819](https://github.com/expo/expo/pull/35819) by [@aleqsio](https://github.com/aleqsio))
|
|
41
|
+
- Drop section polyfill for Android ([#35305](https://github.com/expo/expo/pull/35305) by [@aleqsio](https://github.com/aleqsio))
|
|
42
|
+
- Standardize platform key ordering in `expo-module.config.json`. ([#35003](https://github.com/expo/expo/pull/35003) by [@reichhartd](https://github.com/reichhartd))
|
|
43
|
+
- Dismiss context menu when a menu item is tapped on Android ([#35365](https://github.com/expo/expo/pull/35365) by [@fobos531](https://github.com/fobos531))
|
|
44
|
+
- Migrated SwiftUI views with backward compatible `WithHostingView`. ([#35553](https://github.com/expo/expo/pull/35553) by [@kudo](https://github.com/kudo))
|
|
45
|
+
|
|
13
46
|
## 0.0.2 — 2025-02-11
|
|
14
47
|
|
|
15
48
|
### 🎉 New features
|
|
16
49
|
|
|
50
|
+
- Add `systemImage` prop to Android `Button` component. ([#34862](https://github.com/expo/expo/pull/34862) by [@andrew-levy](https://github.com/andrew-levy))
|
|
51
|
+
- Add `UnwrappedChildren` for nested SwiftUI views. ([#34954](https://github.com/expo/expo/pull/34954) by [@andrew-levy](https://github.com/andrew-levy))
|
|
17
52
|
- Add `color` and `elementColors` props. ([#34666](https://github.com/expo/expo/pull/34666) by [@aleqsio](https://github.com/aleqsio))
|
|
18
53
|
- Add Button component.([#34340](https://github.com/expo/expo/pull/34340) by [@behenate](https://github.com/behenate))
|
|
19
54
|
- Apple TV support and source restructure. ([#34532](https://github.com/expo/expo/pull/34532) by [@douglowder](https://github.com/douglowder))
|
|
20
55
|
- Add `ContextMenu` component. ([#34553](https://github.com/expo/expo/pull/34553) by [@behenate](https://github.com/behenate))
|
|
56
|
+
- Add `ColorPicker` component. ([#34819](https://github.com/expo/expo/pull/34819) by [@andrew-levy](https://github.com/andrew-levy))
|
|
21
57
|
|
|
22
58
|
### 🐛 Bug fixes
|
|
23
59
|
|
|
60
|
+
- Fix flex/width/height props for autosized components. ([#34922](https://github.com/expo/expo/pull/34922) by [@aleqsio](https://github.com/aleqsio))
|
|
24
61
|
- Fix tvOS compilation. ([#34730](https://github.com/expo/expo/pull/34730) by [@douglowder](https://github.com/douglowder))
|
|
62
|
+
- Exclude `ColorPicker` on tvOS. ([#34929](https://github.com/expo/expo/pull/34929) by [@alanjhughes](https://github.com/alanjhughes))
|
|
25
63
|
|
|
26
64
|
### 💡 Others
|
|
27
65
|
|
|
66
|
+
- Add docs ([#34808](https://github.com/expo/expo/pull/34808) by [@aleqsio](https://github.com/aleqsio))
|
|
28
67
|
- [apple] Migrate remaining `expo-module.config.json` to unified platform syntax. ([#34445](https://github.com/expo/expo/pull/34445) by [@reichhartd](https://github.com/reichhartd))
|
|
29
68
|
- Rename the events for the `Switch` component. ([#34577](https://github.com/expo/expo/pull/34577) by [@behenate](https://github.com/behenate))
|
|
69
|
+
- Allow lower case section titles ([#35113](https://github.com/expo/expo/pull/35113) by [@Pflaumenbaum](https://github.com/Pflaumenbaum))
|
|
30
70
|
|
|
31
71
|
## 0.0.1 — 2025-01-21
|
|
32
72
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { requireNativeView } from 'expo';
|
|
2
|
+
import { useCallback } from 'react';
|
|
3
|
+
import { NativeSyntheticEvent, processColor, StyleProp, ViewStyle } from 'react-native';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Props for the ColorPicker component.
|
|
7
|
+
*/
|
|
8
|
+
export type ColorPickerProps = {
|
|
9
|
+
/**
|
|
10
|
+
* The currently selected color in the format `#RRGGBB` or `#RRGGBBAA`.
|
|
11
|
+
*/
|
|
12
|
+
selection: string | null;
|
|
13
|
+
/**
|
|
14
|
+
* A label displayed on the ColorPicker.
|
|
15
|
+
*/
|
|
16
|
+
label?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Callback function that is called when a new color is selected.
|
|
19
|
+
*/
|
|
20
|
+
onValueChanged?: (value: string) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Optional style to apply to the ColorPicker component.
|
|
23
|
+
*/
|
|
24
|
+
style?: StyleProp<ViewStyle>;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the color picker should support opacity.
|
|
27
|
+
*/
|
|
28
|
+
supportsOpacity?: boolean;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type OnValueChangedEvent = NativeSyntheticEvent<{ value: string }>;
|
|
32
|
+
|
|
33
|
+
const ColorPickerNativeView: React.ComponentType<
|
|
34
|
+
Omit<ColorPickerProps, 'selection' | 'onValueChanged'> & {
|
|
35
|
+
selection: ReturnType<typeof processColor>;
|
|
36
|
+
onValueChanged: (event: OnValueChangedEvent) => void;
|
|
37
|
+
}
|
|
38
|
+
> = requireNativeView('ExpoUI', 'ColorPickerView');
|
|
39
|
+
|
|
40
|
+
export function ColorPicker({ selection, onValueChanged, ...restProps }: ColorPickerProps) {
|
|
41
|
+
const onNativeValueChanged = useCallback(
|
|
42
|
+
(event: OnValueChangedEvent) => {
|
|
43
|
+
onValueChanged?.(event.nativeEvent.value);
|
|
44
|
+
},
|
|
45
|
+
[onValueChanged]
|
|
46
|
+
);
|
|
47
|
+
return (
|
|
48
|
+
<ColorPickerNativeView
|
|
49
|
+
selection={processColor(selection || '')}
|
|
50
|
+
onValueChanged={onNativeValueChanged}
|
|
51
|
+
{...restProps}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { requireNativeView } from 'expo';
|
|
2
|
+
import { Children, useMemo } from 'react';
|
|
3
|
+
import { NativeSyntheticEvent } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import { ContextMenuProps, EventHandlers, NativeMenuProps } from '.';
|
|
6
|
+
import { transformChildrenToElementArray } from './utils';
|
|
7
|
+
|
|
8
|
+
const MenuNativeView: React.ComponentType<NativeMenuProps> = requireNativeView(
|
|
9
|
+
'ExpoUI',
|
|
10
|
+
'ContextMenu'
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export function Submenu() {
|
|
14
|
+
return <></>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function Items() {
|
|
18
|
+
return <></>;
|
|
19
|
+
}
|
|
20
|
+
Items.tag = 'Items';
|
|
21
|
+
|
|
22
|
+
export function Trigger(props: { children: React.ReactNode }) {
|
|
23
|
+
return <></>;
|
|
24
|
+
}
|
|
25
|
+
Trigger.tag = 'Trigger';
|
|
26
|
+
|
|
27
|
+
export function Preview(props: { children: React.ReactNode }) {
|
|
28
|
+
return <></>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function ContextMenu(props: ContextMenuProps) {
|
|
32
|
+
const eventHandlersMap: EventHandlers = {};
|
|
33
|
+
const initialChildren = Children.map(
|
|
34
|
+
props.children as any,
|
|
35
|
+
(c: { type: { tag: string }; props: { children: React.ReactNode } }) =>
|
|
36
|
+
c.type.tag === Items.tag ? c.props.children : null
|
|
37
|
+
);
|
|
38
|
+
const processedElements = useMemo(
|
|
39
|
+
() => transformChildrenToElementArray(initialChildren, eventHandlersMap),
|
|
40
|
+
[initialChildren]
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const activationElement = Children.map(
|
|
44
|
+
props.children as any,
|
|
45
|
+
(c: { type: { tag: string }; props: { children: React.ReactNode } }) =>
|
|
46
|
+
c.type.tag === Trigger.tag ? c.props.children : null
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const createEventHandler =
|
|
50
|
+
(handlerType: string) => (e: NativeSyntheticEvent<{ contextMenuElementID: string }>) => {
|
|
51
|
+
const handler = eventHandlersMap[e.nativeEvent.contextMenuElementID]?.[handlerType];
|
|
52
|
+
handler?.(e);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<MenuNativeView
|
|
57
|
+
style={props.style}
|
|
58
|
+
elements={processedElements}
|
|
59
|
+
onContextMenuButtonPressed={createEventHandler('onPress')}
|
|
60
|
+
onContextMenuSwitchValueChanged={createEventHandler('onValueChange')}
|
|
61
|
+
onContextMenuPickerOptionSelected={createEventHandler('onOptionSelected')}
|
|
62
|
+
{...props}>
|
|
63
|
+
{activationElement}
|
|
64
|
+
</MenuNativeView>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
ContextMenu.Trigger = Trigger;
|
|
69
|
+
ContextMenu.Preview = Preview;
|
|
70
|
+
ContextMenu.Items = Items;
|
|
71
|
+
|
|
72
|
+
export { ContextMenu };
|
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
import { requireNativeView } from 'expo';
|
|
2
|
-
import
|
|
2
|
+
import { ComponentType, Children, ReactElement, ReactNode, useMemo } from 'react';
|
|
3
3
|
import { NativeSyntheticEvent, StyleProp, ViewStyle } from 'react-native';
|
|
4
4
|
|
|
5
|
+
import { MenuElement, transformChildrenToElementArray } from './utils';
|
|
5
6
|
import { ButtonProps } from '../Button';
|
|
6
7
|
import { PickerProps } from '../Picker';
|
|
7
8
|
import { SwitchProps } from '../Switch';
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
const MenuNativeView: ComponentType<NativeMenuProps> = requireNativeView(
|
|
11
|
+
'ExpoUI',
|
|
12
|
+
'ContextMenu'
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const MenuNativeTriggerView: ComponentType<object> = requireNativeView(
|
|
16
|
+
'ExpoUI',
|
|
17
|
+
'ContextMenuActivationElement'
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const MenuNativePreviewView: ComponentType<object> = requireNativeView(
|
|
21
|
+
'ExpoUI',
|
|
22
|
+
'ContextMenuPreview'
|
|
23
|
+
);
|
|
9
24
|
|
|
10
25
|
type SubmenuElement =
|
|
11
26
|
| ReactElement<ButtonProps>
|
|
@@ -13,18 +28,21 @@ type SubmenuElement =
|
|
|
13
28
|
| ReactElement<PickerProps>
|
|
14
29
|
| ReactElement<SubmenuProps>;
|
|
15
30
|
|
|
16
|
-
type ContentChildren = SubmenuElement | SubmenuElement[];
|
|
17
|
-
|
|
18
31
|
export type ContextMenuContentProps = {
|
|
19
|
-
children:
|
|
32
|
+
children: SubmenuElement | SubmenuElement[];
|
|
20
33
|
};
|
|
21
34
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @hidden
|
|
37
|
+
*/
|
|
38
|
+
export type EventHandlers = Record<
|
|
39
|
+
string,
|
|
40
|
+
Record<string, (event: NativeSyntheticEvent<any>) => void>
|
|
41
|
+
>;
|
|
27
42
|
|
|
43
|
+
/**
|
|
44
|
+
* @hidden
|
|
45
|
+
*/
|
|
28
46
|
export type ContextMenuElementBase = { contextMenuElementID: string };
|
|
29
47
|
|
|
30
48
|
/**
|
|
@@ -38,13 +56,6 @@ export type ActivationMethod = 'singlePress' | 'longPress';
|
|
|
38
56
|
* Props of the `ContextMenu` component.
|
|
39
57
|
*/
|
|
40
58
|
export type ContextMenuProps = {
|
|
41
|
-
/**
|
|
42
|
-
* Items visible inside the context menu. The items should be wrapped in a `React.Fragment`.
|
|
43
|
-
* `Button`, `Switch` and `Submenu` components are supported on both Android and iOS.
|
|
44
|
-
* The `Picker` component is supported only on iOS. Remember to use components from the `@expo/ui` library.
|
|
45
|
-
*/
|
|
46
|
-
Items: React.ReactElement<ContextMenuContentProps>;
|
|
47
|
-
|
|
48
59
|
/**
|
|
49
60
|
* Determines how the context menu will be activated.
|
|
50
61
|
*
|
|
@@ -58,6 +69,13 @@ export type ContextMenuProps = {
|
|
|
58
69
|
*/
|
|
59
70
|
children: ReactNode;
|
|
60
71
|
|
|
72
|
+
/**
|
|
73
|
+
* The color of the container holding the context menu items.
|
|
74
|
+
*
|
|
75
|
+
* @platform android
|
|
76
|
+
*/
|
|
77
|
+
color?: string;
|
|
78
|
+
|
|
61
79
|
/**
|
|
62
80
|
* Optional styles to apply to the `ContextMenu`
|
|
63
81
|
*/
|
|
@@ -71,14 +89,17 @@ export type SubmenuProps = {
|
|
|
71
89
|
/**
|
|
72
90
|
* The button that will be used to expand the submenu. On Android the `text` prop of the `Button` will be used as a section title.
|
|
73
91
|
*/
|
|
74
|
-
button:
|
|
92
|
+
button: ReactElement<ButtonProps>;
|
|
75
93
|
/**
|
|
76
94
|
* Children of the submenu. Only `Button`, `Switch`, `Picker` and `Submenu` elements should be used.
|
|
77
95
|
*/
|
|
78
|
-
children:
|
|
96
|
+
children: ReactNode;
|
|
79
97
|
};
|
|
80
98
|
|
|
81
|
-
|
|
99
|
+
/**
|
|
100
|
+
* @hidden
|
|
101
|
+
*/
|
|
102
|
+
export type NativeMenuProps = ContextMenuProps & {
|
|
82
103
|
elements: MenuElement[];
|
|
83
104
|
onContextMenuButtonPressed: (
|
|
84
105
|
event: NativeSyntheticEvent<{ contextMenuElementID: string }>
|
|
@@ -98,11 +119,6 @@ type NativeMenuProps = ContextMenuProps & {
|
|
|
98
119
|
) => void;
|
|
99
120
|
};
|
|
100
121
|
|
|
101
|
-
const MenuNativeView: React.ComponentType<NativeMenuProps> = requireNativeView(
|
|
102
|
-
'ExpoUI',
|
|
103
|
-
'ContextMenu'
|
|
104
|
-
);
|
|
105
|
-
|
|
106
122
|
/**
|
|
107
123
|
* The `Submenu` component is used to create a nested context menu. Submenus can be infinitely nested.
|
|
108
124
|
* Android does not support nesting in the context menu. All the submenus will be flat-mapped into a single level with multiple titled sections.
|
|
@@ -111,6 +127,30 @@ export function Submenu(props: SubmenuProps) {
|
|
|
111
127
|
return <></>;
|
|
112
128
|
}
|
|
113
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Items visible inside the context menu. Pass input components as immidiate children of the tag.
|
|
132
|
+
* `Button`, `Switch` and `Submenu` components are supported on both Android and iOS.
|
|
133
|
+
* The `Picker` component is supported only on iOS. Remember to use components from the `@expo/ui` library.
|
|
134
|
+
*/
|
|
135
|
+
export function Items(props: { children: React.ReactNode }) {
|
|
136
|
+
return <></>;
|
|
137
|
+
}
|
|
138
|
+
Items.tag = 'Items';
|
|
139
|
+
/**
|
|
140
|
+
* The component visible all the time that triggers the menu when tapped or long-pressed.
|
|
141
|
+
*/
|
|
142
|
+
export function Trigger(props: { children: React.ReactNode }) {
|
|
143
|
+
return <MenuNativeTriggerView {...props} />;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The component visible above the menu when it is opened.
|
|
148
|
+
* @platform ios
|
|
149
|
+
*/
|
|
150
|
+
export function Preview(props: { children: React.ReactNode }) {
|
|
151
|
+
return <MenuNativePreviewView {...props} />;
|
|
152
|
+
}
|
|
153
|
+
|
|
114
154
|
/**
|
|
115
155
|
* `ContextMenu` allows you to create a context menu, which can be used to provide additional options to the user.
|
|
116
156
|
*
|
|
@@ -120,18 +160,22 @@ export function Submenu(props: SubmenuProps) {
|
|
|
120
160
|
* - Android does not support nesting in the context menu. All the submenus will be flat-mapped into a single level with multiple sections. The `title` prop of the `Button`, which opens the submenu on iOS will be used as a section title.
|
|
121
161
|
* - Android does not support showing a `Picker` element in the context menu.
|
|
122
162
|
*/
|
|
123
|
-
|
|
163
|
+
function ContextMenu(props: ContextMenuProps) {
|
|
124
164
|
const eventHandlersMap: EventHandlers = {};
|
|
125
|
-
const initialChildren =
|
|
165
|
+
const initialChildren = Children.map(
|
|
166
|
+
props.children as any,
|
|
167
|
+
(c: { type: { tag: string }; props: { children: React.ReactNode } }) =>
|
|
168
|
+
c.type.tag === Items.tag ? c.props.children : null
|
|
169
|
+
);
|
|
126
170
|
const processedElements = useMemo(
|
|
127
171
|
() => transformChildrenToElementArray(initialChildren, eventHandlersMap),
|
|
128
172
|
[initialChildren]
|
|
129
173
|
);
|
|
130
174
|
|
|
131
175
|
const createEventHandler =
|
|
132
|
-
(handlerType: string) => (
|
|
133
|
-
const handler = eventHandlersMap[
|
|
134
|
-
handler?.(
|
|
176
|
+
(handlerType: string) => (event: NativeSyntheticEvent<{ contextMenuElementID: string }>) => {
|
|
177
|
+
const handler = eventHandlersMap[event.nativeEvent.contextMenuElementID]?.[handlerType];
|
|
178
|
+
handler?.(event);
|
|
135
179
|
};
|
|
136
180
|
|
|
137
181
|
return (
|
|
@@ -145,3 +189,9 @@ export function ContextMenu(props: ContextMenuProps) {
|
|
|
145
189
|
/>
|
|
146
190
|
);
|
|
147
191
|
}
|
|
192
|
+
|
|
193
|
+
ContextMenu.Trigger = Trigger;
|
|
194
|
+
ContextMenu.Preview = Preview;
|
|
195
|
+
ContextMenu.Items = Items;
|
|
196
|
+
|
|
197
|
+
export { ContextMenu };
|
|
@@ -52,18 +52,22 @@ function processChildElement(
|
|
|
52
52
|
const uuid = expo.uuidv4();
|
|
53
53
|
|
|
54
54
|
if (child.type === Button) {
|
|
55
|
+
// @ts-expect-error TODO TS2345: Argument of type unknown is not assignable to parameter of type SubmenuProps
|
|
55
56
|
return createButtonElement(uuid, child.props, eventHandlersMap);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
if (child.type === Switch) {
|
|
60
|
+
// @ts-expect-error TODO TS2345: Argument of type unknown is not assignable to parameter of type SubmenuProps
|
|
59
61
|
return createSwitchElement(uuid, child.props, eventHandlersMap);
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
if (child.type === Picker) {
|
|
65
|
+
// @ts-expect-error TODO TS2345: Argument of type unknown is not assignable to parameter of type SubmenuProps
|
|
63
66
|
return createPickerElement(uuid, child.props, eventHandlersMap);
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
if (isSubmenuComponent(child)) {
|
|
70
|
+
// @ts-expect-error TODO TS2345: Argument of type unknown is not assignable to parameter of type SubmenuProps
|
|
67
71
|
return createSubmenuElement(uuid, child.props, eventHandlersMap);
|
|
68
72
|
}
|
|
69
73
|
|