@expo/ui 1.0.0-canary-20250306-d9d3e02 → 1.0.0-canary-20250331-817737a

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 (62) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +4 -0
  3. package/android/src/main/java/expo/modules/ui/PickerView.kt +3 -0
  4. package/android/src/main/java/expo/modules/ui/TextInputView.kt +75 -0
  5. package/android/src/main/java/expo/modules/ui/menu/ContextMenu.kt +7 -3
  6. package/build/components/BottomSheet/index.d.ts +9 -0
  7. package/build/components/BottomSheet/index.d.ts.map +1 -0
  8. package/build/components/BottomSheet/index.ios.d.ts +11 -0
  9. package/build/components/BottomSheet/index.ios.d.ts.map +1 -0
  10. package/build/components/Button/index.d.ts +1 -1
  11. package/build/components/ContextMenu/index.android.d.ts +23 -0
  12. package/build/components/ContextMenu/index.android.d.ts.map +1 -0
  13. package/build/components/ContextMenu/index.d.ts +54 -20
  14. package/build/components/ContextMenu/index.d.ts.map +1 -1
  15. package/build/components/DatePicker/index.d.ts +6 -6
  16. package/build/components/Label/index.d.ts +28 -0
  17. package/build/components/Label/index.d.ts.map +1 -0
  18. package/build/components/Label/index.ios.d.ts +10 -0
  19. package/build/components/Label/index.ios.d.ts.map +1 -0
  20. package/build/components/List/index.d.ts +87 -0
  21. package/build/components/List/index.d.ts.map +1 -0
  22. package/build/components/List/index.ios.d.ts +9 -0
  23. package/build/components/List/index.ios.d.ts.map +1 -0
  24. package/build/components/Picker/index.d.ts +8 -5
  25. package/build/components/Picker/index.d.ts.map +1 -1
  26. package/build/components/Progress/index.d.ts +1 -1
  27. package/build/components/Section/index.d.ts +10 -2
  28. package/build/components/Section/index.d.ts.map +1 -1
  29. package/build/components/Slider/index.d.ts +5 -2
  30. package/build/components/Slider/index.d.ts.map +1 -1
  31. package/build/components/Switch/index.d.ts +18 -36
  32. package/build/components/Switch/index.d.ts.map +1 -1
  33. package/build/components/TextInput/index.d.ts +24 -1
  34. package/build/components/TextInput/index.d.ts.map +1 -1
  35. package/build/src/types.d.ts +4 -2
  36. package/build/src/types.d.ts.map +1 -1
  37. package/components/BottomSheet/index.ios.tsx +34 -0
  38. package/components/BottomSheet/index.tsx +12 -0
  39. package/components/Button/index.tsx +1 -1
  40. package/components/ContextMenu/index.android.tsx +72 -0
  41. package/components/ContextMenu/index.tsx +67 -33
  42. package/components/DatePicker/index.tsx +6 -6
  43. package/components/Label/index.ios.tsx +16 -0
  44. package/components/Label/index.tsx +34 -0
  45. package/components/List/index.ios.tsx +35 -0
  46. package/components/List/index.tsx +99 -0
  47. package/components/Picker/index.tsx +8 -5
  48. package/components/Progress/index.tsx +1 -1
  49. package/components/Section/index.tsx +10 -2
  50. package/components/Slider/index.tsx +5 -2
  51. package/components/Switch/index.tsx +37 -54
  52. package/components/TextInput/index.tsx +30 -3
  53. package/ios/BottomSheetView.swift +82 -0
  54. package/ios/ContextMenu/ContextMenu.swift +65 -2
  55. package/ios/ContextMenu/ContextMenuRecords.swift +6 -0
  56. package/ios/ExpoUIModule.swift +5 -0
  57. package/ios/Label.swift +24 -0
  58. package/ios/List.swift +122 -0
  59. package/ios/PickerView.swift +32 -27
  60. package/ios/SectionView.swift +1 -1
  61. package/package.json +3 -4
  62. package/src/types.ts +4 -2
@@ -0,0 +1,35 @@
1
+ import { requireNativeView } from 'expo';
2
+
3
+ import { ListProps, NativeListProps } from '.';
4
+
5
+ const ListNativeView: React.ComponentType<NativeListProps> | null =
6
+ requireNativeView<NativeListProps>('ExpoUI', 'ListView');
7
+
8
+ function transformListProps(props: Omit<ListProps, 'children'>): Omit<NativeListProps, 'children'> {
9
+ return {
10
+ ...props,
11
+ onDeleteItem: ({ nativeEvent: { index } }) => props?.onDeleteItem?.(index),
12
+ onMoveItem: ({ nativeEvent: { from, to } }) => props?.onMoveItem?.(from, to),
13
+ onSelectionChange: ({ nativeEvent: { selection } }) => props?.onSelectionChange?.(selection),
14
+ };
15
+ }
16
+
17
+ /**
18
+ * A list component that renders its children using a native SwiftUI list.
19
+ * @param {ListProps} props - The properties for the list component.
20
+ * @returns {JSX.Element | null} The rendered list with its children or null if the platform is unsupported.
21
+ * @platform ios
22
+ */
23
+ export function List(props: ListProps) {
24
+ const { children, ...nativeProps } = props;
25
+
26
+ if (!ListNativeView) {
27
+ return null;
28
+ }
29
+
30
+ return (
31
+ <ListNativeView {...transformListProps(nativeProps)} style={[props.style, { flex: 1 }]}>
32
+ {children}
33
+ </ListNativeView>
34
+ );
35
+ }
@@ -0,0 +1,99 @@
1
+ import { StyleProp, ViewStyle } from 'react-native';
2
+
3
+ import { ViewEvent } from '../../src/types';
4
+
5
+ export type ListStyle = 'automatic' | 'plain' | 'inset' | 'insetGrouped' | 'grouped' | 'sidebar';
6
+
7
+ export interface ListProps {
8
+ /**
9
+ * Custom style for the container wrapping the list.
10
+ */
11
+ style?: StyleProp<ViewStyle>;
12
+
13
+ /**
14
+ * One of the predefined ListStyle types in SwiftUI.
15
+ * @default 'automatic'
16
+ */
17
+ listStyle?: ListStyle;
18
+
19
+ /**
20
+ * Allows the selection of list items.
21
+ * @default false
22
+ */
23
+ selectEnabled?: boolean;
24
+
25
+ /**
26
+ * Enables reordering of list items.
27
+ * @default false
28
+ */
29
+ moveEnabled?: boolean;
30
+
31
+ /**
32
+ * Allows the deletion of list items.
33
+ * @default false
34
+ */
35
+ deleteEnabled?: boolean;
36
+
37
+ /**
38
+ * Makes the list scrollable.
39
+ * @default true
40
+ * @platform ios 16.0+
41
+ */
42
+ scrollEnabled?: boolean;
43
+
44
+ /**
45
+ * Enables SwiftUI edit mode.
46
+ * @default false
47
+ */
48
+ editModeEnabled?: boolean;
49
+
50
+ /**
51
+ * The children elements to be rendered inside the list.
52
+ */
53
+ children: React.ReactNode;
54
+
55
+ /**
56
+ * Callback triggered when an item is deleted from the list.
57
+ */
58
+ onDeleteItem?: (index: number) => void;
59
+
60
+ /**
61
+ * Callback triggered when an item is moved in the list.
62
+ */
63
+ onMoveItem?: (from: number, to: number) => void;
64
+
65
+ /**
66
+ * Callback triggered when the selection changes in a list.
67
+ */
68
+ onSelectionChange?: (selection: number[]) => void;
69
+ }
70
+
71
+ /**
72
+ * DeleteItemEvent represents an event triggered when an item is deleted from the list.
73
+ */
74
+ type DeleteItemEvent = ViewEvent<'onDeleteItem', { index: number }>;
75
+ /**
76
+ * MoveItemEvent represents an event triggered when an item is moved in the list.
77
+ */
78
+ type MoveItemEvent = ViewEvent<'onMoveItem', { from: number; to: number }>;
79
+ /**
80
+ * SelectItemEvent represents an event triggered when the selection changes in a list.
81
+ */
82
+ type SelectItemEvent = ViewEvent<'onSelectionChange', { selection: number[] }>;
83
+
84
+ export type NativeListProps = Omit<ListProps, 'onDeleteItem' | 'onMoveItem' | 'onSelectionChange'> &
85
+ DeleteItemEvent &
86
+ MoveItemEvent &
87
+ SelectItemEvent & {
88
+ children: React.ReactNode;
89
+ };
90
+
91
+ /**
92
+ * A list component that renders its children using a native SwiftUI list.
93
+ * @param {ListProps} props - The properties for the list component.
94
+ * @returns {JSX.Element | null} The rendered list with its children or null if the platform is unsupported.
95
+ * @platform ios
96
+ */
97
+ export function List({ children }: ListProps) {
98
+ return children;
99
+ }
@@ -33,8 +33,8 @@ export type PickerProps = {
33
33
  */
34
34
  selectedIndex: number | null;
35
35
  /**
36
- * A label displayed on the picker when in `menu` variant inside a form section on iOS.
37
- * @platform iOS
36
+ * A label displayed on the picker when in `'menu'` variant inside a form section on iOS.
37
+ * @platform ios
38
38
  */
39
39
  label?: string;
40
40
  /**
@@ -43,10 +43,10 @@ export type PickerProps = {
43
43
  onOptionSelected?: (event: { nativeEvent: { index: number; label: string } }) => void;
44
44
  /**
45
45
  * The variant of the picker, which determines its appearance and behavior.
46
- * The 'wheel' and 'menu' variants are iOS only, the 'radio' variant is Android only.
46
+ * The `'wheel'`, `'inline'`, `'palette'` and `'menu'` variants are iOS only, the `'radio'` variant is Android only. The `'inline'` variant can only be used inside sections or lists. The `'palette'` variant displays differently inside menus.
47
47
  * @default 'segmented'
48
48
  */
49
- variant?: 'wheel' | 'segmented' | 'menu' | 'radio';
49
+ variant?: 'wheel' | 'segmented' | 'menu' | 'radio' | 'inline' | 'palette';
50
50
  /**
51
51
  * Optional style to apply to the picker component.
52
52
  */
@@ -58,7 +58,7 @@ export type PickerProps = {
58
58
  */
59
59
  elementColors?: PickerElementColors;
60
60
  /**
61
- * Picker color. On iOS it only applies to the `menu` variant.
61
+ * Picker color. On iOS it only applies to the `'menu'` variant.
62
62
  */
63
63
  color?: string;
64
64
  };
@@ -70,6 +70,9 @@ const PickerNativeView: React.ComponentType<PickerProps> = requireNativeView(
70
70
 
71
71
  type NativePickerProps = PickerProps;
72
72
 
73
+ /**
74
+ * @hidden
75
+ */
73
76
  export function transformPickerProps(props: PickerProps): NativePickerProps {
74
77
  return {
75
78
  ...props,
@@ -16,7 +16,7 @@ export type ProgressProps = {
16
16
  */
17
17
  style?: StyleProp<ViewStyle>;
18
18
  /**
19
- * The current progress value of the slider. This is a number between 0 and 1.
19
+ * The current progress value of the slider. This is a number between `0` and `1`.
20
20
  */
21
21
  progress?: number | null;
22
22
  /**
@@ -1,11 +1,19 @@
1
1
  import { StyleProp, ViewStyle } from 'react-native';
2
2
 
3
3
  export type SectionProps = {
4
- style?: StyleProp<ViewStyle>;
5
- title: string;
4
+ /**
5
+ * @note On iOS, section titles are usually capitalized for consistency with platform conventions.
6
+ */
7
+ title?: string;
6
8
  children: any;
9
+ style?: StyleProp<ViewStyle>;
7
10
  };
8
11
 
12
+ /**
13
+ * Section component uses the native [Section](https://developer.apple.com/documentation/swiftui/section) component.
14
+ * It has no intrinsic dimensions, so it needs explicit height or flex set to display content (like ScrollView).
15
+ * @platform ios
16
+ */
9
17
  export function Section({ children }: SectionProps) {
10
18
  return children;
11
19
  }
@@ -26,12 +26,12 @@ export type SliderProps = {
26
26
  */
27
27
  value?: number;
28
28
  /**
29
- * The number of steps between the minimum and maximum values. 0 signifies infinite steps.
29
+ * The number of steps between the minimum and maximum values, `0` signifies infinite steps.
30
30
  * @default 0
31
31
  */
32
32
  steps?: number;
33
33
  /**
34
- * The mininum value of the slider. Updating this value does not trigger callbacks if the current value is below `min`.
34
+ * The minimum value of the slider. Updating this value does not trigger callbacks if the current value is below `min`.
35
35
  * @default 0
36
36
  */
37
37
  min?: number;
@@ -63,6 +63,9 @@ const SliderNativeView: React.ComponentType<NativeSliderProps> = requireNativeVi
63
63
  'SliderView'
64
64
  );
65
65
 
66
+ /**
67
+ * @hidden
68
+ */
66
69
  export function transformSliderProps(props: SliderProps): NativeSliderProps {
67
70
  return {
68
71
  ...props,
@@ -1,49 +1,27 @@
1
1
  import { requireNativeView } from 'expo';
2
2
  import { NativeSyntheticEvent, StyleProp, ViewStyle } from 'react-native';
3
3
 
4
+ // @docsMissing
5
+ /**
6
+ * Only for switch.
7
+ */
4
8
  type SwitchElementColors = {
5
- /**
6
- * Only for switch.
7
- */
8
9
  checkedThumbColor?: string;
9
- /**
10
- * Only for switch.
11
- */
12
10
  checkedTrackColor?: string;
13
- /**
14
- * Only for switch.
15
- */
16
11
  uncheckedThumbColor?: string;
17
- /**
18
- * Only for switch.
19
- */
20
12
  uncheckedTrackColor?: string;
21
13
  };
22
14
 
15
+ // @docsMissing
16
+ /**
17
+ * Only for checkbox.
18
+ */
23
19
  type CheckboxElementColors = {
24
- /**
25
- * Only for checkbox.
26
- */
27
20
  checkedColor?: string;
28
- /**
29
- * Only for checkbox.
30
- */
31
21
  disabledCheckedColor?: string;
32
- /**
33
- * Only for checkbox.
34
- */
35
22
  uncheckedColor?: string;
36
- /**
37
- * Only for checkbox.
38
- */
39
23
  disabledUncheckedColor?: string;
40
- /**
41
- * Only for checkbox.
42
- */
43
24
  checkmarkColor?: string;
44
- /**
45
- * Only for checkbox.
46
- */
47
25
  disabledIndeterminateColor?: string;
48
26
  };
49
27
 
@@ -61,7 +39,7 @@ export type SwitchProps = {
61
39
  label?: string;
62
40
 
63
41
  /**
64
- * Type of the switch component. Can be 'checkbox', 'switch', or 'button'. The 'button' style is iOS only.
42
+ * Type of the switch component. Can be `'checkbox'`, `'switch'`, or `'button'`. The `'button'` style is iOS only.
65
43
  * @default 'switch'
66
44
  */
67
45
  variant?: 'checkbox' | 'switch' | 'button';
@@ -74,31 +52,33 @@ export type SwitchProps = {
74
52
  */
75
53
  style?: StyleProp<ViewStyle>;
76
54
  /**
77
- * Picker color. On iOS it only applies to the `menu` variant.
55
+ * Picker color. On iOS, it only applies to the `menu` variant.
78
56
  */
79
57
  color?: string;
80
- } & (
81
- | {
82
- variant?: 'switch';
83
- /**
84
- * Colors for switch's core elements.
85
- * @platform android
86
- */
87
- elementColors?: SwitchElementColors;
88
- }
89
- | {
90
- variant: 'checkbox';
91
- /**
92
- * Colors for checkbox core elements.
93
- * @platform android
94
- */
95
- elementColors?: CheckboxElementColors;
96
- }
97
- | {
98
- variant: 'button';
99
- elementColors?: undefined;
100
- }
101
- );
58
+ } & (SwitchSwitchVariantProps | SwitchCheckboxVariantProps | SwitchButtonVariantProps);
59
+
60
+ export type SwitchSwitchVariantProps = {
61
+ variant?: 'switch';
62
+ /**
63
+ * Colors for switch's core elements.
64
+ * @platform android
65
+ */
66
+ elementColors?: SwitchElementColors;
67
+ };
68
+
69
+ export type SwitchCheckboxVariantProps = {
70
+ variant: 'checkbox';
71
+ /**
72
+ * Colors for checkbox core elements.
73
+ * @platform android
74
+ */
75
+ elementColors?: CheckboxElementColors;
76
+ };
77
+
78
+ export type SwitchButtonVariantProps = {
79
+ variant: 'button';
80
+ elementColors?: undefined;
81
+ };
102
82
 
103
83
  type NativeSwitchProps = Omit<SwitchProps, 'onValueChange'> & {
104
84
  onValueChange: (event: NativeSyntheticEvent<{ value: boolean }>) => void;
@@ -127,6 +107,9 @@ function getElementColors(props: SwitchProps) {
127
107
  return props.elementColors;
128
108
  }
129
109
 
110
+ /**
111
+ * @hidden
112
+ */
130
113
  export function transformSwitchProps(props: SwitchProps): NativeSwitchProps {
131
114
  return {
132
115
  ...props,
@@ -3,6 +3,9 @@ import { StyleProp, ViewStyle } from 'react-native';
3
3
 
4
4
  import { ViewEvent } from '../../src/types';
5
5
 
6
+ /**
7
+ * @hidden Not used anywhere yet.
8
+ */
6
9
  export type TextInputRole = 'default' | 'cancel' | 'destructive';
7
10
 
8
11
  /**
@@ -34,6 +37,27 @@ export type TextInputProps = {
34
37
  numberOfLines?: number;
35
38
  /**
36
39
  * Determines which keyboard to open, e.g., numeric.
40
+ *
41
+ * Types that work on both platforms:
42
+ * - default
43
+ * - numeric
44
+ * - email-address
45
+ * - phone-pad
46
+ * - decimal-pad
47
+ * - ascii-capable
48
+ * - url
49
+ *
50
+ * Types that only work on Android:
51
+ * - password
52
+ * - password-numeric
53
+ *
54
+ * Types that only work on iOS:
55
+ * - numbers-and-punctuation
56
+ * - name-phone-pad
57
+ * - twitter
58
+ * - web-search
59
+ * - ascii-capable-number-pad
60
+ *
37
61
  * @default default
38
62
  */
39
63
  keyboardType?:
@@ -58,7 +82,7 @@ export type TextInputProps = {
58
82
 
59
83
  export type NativeTextInputProps = Omit<TextInputProps, 'onChangeText'> & {} & ViewEvent<
60
84
  'onValueChanged',
61
- { value: string; eventIndex: number }
85
+ { value: string }
62
86
  >;
63
87
 
64
88
  // We have to work around the `role` and `onPress` props being reserved by React Native.
@@ -67,11 +91,14 @@ const TextInputNativeView: React.ComponentType<NativeTextInputProps> = requireNa
67
91
  'TextInputView'
68
92
  );
69
93
 
94
+ /**
95
+ * @hidden
96
+ */
70
97
  function transformTextInputProps(props: TextInputProps): NativeTextInputProps {
71
98
  return {
72
99
  ...props,
73
- onValueChanged: (e) => {
74
- props.onChangeText?.(e.nativeEvent.value);
100
+ onValueChanged: (event) => {
101
+ props.onChangeText?.(event.nativeEvent.value);
75
102
  },
76
103
  };
77
104
  }
@@ -0,0 +1,82 @@
1
+ // Copyright 2025-present 650 Industries. All rights reserved.
2
+
3
+ import SwiftUI
4
+ import ExpoModulesCore
5
+
6
+ class BottomSheetProps: ExpoSwiftUI.ViewProps {
7
+ @Field var isOpened: Bool = false
8
+ var onIsOpenedChange = EventDispatcher()
9
+ }
10
+
11
+ struct HeightPreferenceKey: PreferenceKey {
12
+ static var defaultValue: CGFloat?
13
+
14
+ static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) {
15
+ guard let nextValue = nextValue() else {
16
+ return
17
+ }
18
+ value = nextValue
19
+ }
20
+ }
21
+
22
+ private struct ReadHeightModifier: ViewModifier {
23
+ private var sizeView: some View {
24
+ GeometryReader { geometry in
25
+ Color.clear.preference(key: HeightPreferenceKey.self, value: geometry.size.height)
26
+ }
27
+ }
28
+
29
+ func body(content: Content) -> some View {
30
+ content.background(sizeView)
31
+ }
32
+ }
33
+
34
+ struct BottomSheetView: ExpoSwiftUI.View {
35
+ @EnvironmentObject var props: BottomSheetProps
36
+
37
+ @State private var isOpened = true
38
+ @State var height: CGFloat = 0
39
+
40
+ var body: some View {
41
+ if #available(iOS 16.0, tvOS 16.0, *) {
42
+ Rectangle().hidden()
43
+ .sheet(isPresented: $isOpened) {
44
+ Children()
45
+ .modifier(ReadHeightModifier())
46
+ .onPreferenceChange(HeightPreferenceKey.self) { height in
47
+ if let height {
48
+ self.height = height
49
+ }
50
+ }
51
+ .presentationDetents([.height(self.height)])
52
+ }
53
+ .onChange(of: isOpened, perform: { newIsOpened in
54
+ if props.isOpened == newIsOpened {
55
+ return
56
+ }
57
+ props.onIsOpenedChange([
58
+ "isOpened": newIsOpened
59
+ ])
60
+ })
61
+ .onReceive(props.objectWillChange, perform: {
62
+ isOpened = props.isOpened
63
+ })
64
+ } else {
65
+ Rectangle().hidden()
66
+ .sheet(isPresented: $isOpened) {
67
+ Children()
68
+ }
69
+ .onChange(of: isOpened, perform: { newIsOpened in
70
+ if props.isOpened == newIsOpened {
71
+ return
72
+ }
73
+ props.onIsOpenedChange([
74
+ "isOpened": newIsOpened
75
+ ])
76
+ })
77
+ .onReceive(props.objectWillChange, perform: {
78
+ isOpened = props.isOpened
79
+ })
80
+ }
81
+ }
82
+ }
@@ -87,14 +87,77 @@ struct LongPressContextMenu<ActivationElement: View>: View {
87
87
  }
88
88
  }
89
89
 
90
+ struct LongPressContextMenuWithPreview<ActivationElement: View, Preview: View>: View {
91
+ let elements: [ContextMenuElement]?
92
+ let activationElement: ActivationElement
93
+ let preview: Preview
94
+ let props: ContextMenuProps?
95
+
96
+ var body: some View {
97
+ if #available(iOS 16.0, tvOS 16.0, *) {
98
+ activationElement.contextMenu(menuItems: {
99
+ MenuItems(fromElements: elements, props: props)
100
+ }, preview: {
101
+ preview
102
+ })
103
+ } else {
104
+ activationElement.contextMenu(menuItems: {
105
+ MenuItems(fromElements: elements, props: props)
106
+ })
107
+ }
108
+ }
109
+ }
110
+
111
+ struct ContextMenuPreview: ExpoSwiftUI.View {
112
+ @EnvironmentObject var props: ContextMenuPreviewProps
113
+
114
+ var body: some View {
115
+ Children()
116
+ }
117
+ }
118
+
119
+ struct ContextMenuActivationElement: ExpoSwiftUI.View {
120
+ @EnvironmentObject var props: ContextMenuActivationElementProps
121
+
122
+ var body: some View {
123
+ Children()
124
+ }
125
+ }
126
+
90
127
  struct ContextMenu: ExpoSwiftUI.View {
91
128
  @EnvironmentObject var props: ContextMenuProps
92
129
 
93
130
  var body: some View {
94
131
  if props.activationMethod == .singlePress {
95
- SinglePressContextMenu(elements: props.elements, activationElement: Children(), props: props)
132
+ let activationElement = props.children?.filter({
133
+ $0.view is ExpoSwiftUI.HostingView<ContextMenuActivationElementProps, ContextMenuActivationElement>
134
+ })
135
+ SinglePressContextMenu(
136
+ elements: props.elements,
137
+ activationElement: UnwrappedChildren(children: activationElement),
138
+ props: props
139
+ )
96
140
  } else {
97
- LongPressContextMenu(elements: props.elements, activationElement: Children(), props: props)
141
+ let preview = props.children?.filter({
142
+ $0.view is ExpoSwiftUI.HostingView<ContextMenuPreviewProps, ContextMenuPreview>
143
+ })
144
+ let activationElement = props.children?.filter({
145
+ $0.view is ExpoSwiftUI.HostingView<ContextMenuActivationElementProps, ContextMenuActivationElement>
146
+ })
147
+ if preview?.count ?? 0 > 0 {
148
+ LongPressContextMenuWithPreview(
149
+ elements: props.elements,
150
+ activationElement: UnwrappedChildren(children: activationElement),
151
+ preview: UnwrappedChildren(children: preview),
152
+ props: props
153
+ )
154
+ } else {
155
+ LongPressContextMenu(
156
+ elements: props.elements,
157
+ activationElement: UnwrappedChildren(children: activationElement),
158
+ props: props
159
+ )
160
+ }
98
161
  }
99
162
  }
100
163
  }
@@ -27,3 +27,9 @@ internal class ContextMenuProps: ExpoSwiftUI.ViewProps {
27
27
  var onContextMenuSwitchCheckedChanged = EventDispatcher()
28
28
  @Field var activationMethod: ActivationMethod? = .singlePress
29
29
  }
30
+
31
+ internal class ContextMenuPreviewProps: ExpoSwiftUI.ViewProps {
32
+ }
33
+
34
+ internal class ContextMenuActivationElementProps: ExpoSwiftUI.ViewProps {
35
+ }
@@ -10,12 +10,17 @@ public class ExpoUIModule: Module {
10
10
  View(PickerView.self)
11
11
  View(SwitchView.self)
12
12
  View(SectionView.self)
13
+ View(BottomSheetView.self)
13
14
  View(SliderView.self)
14
15
  View(ExpoUI.ContextMenu.self)
16
+ View(ExpoUI.ContextMenuActivationElement.self)
17
+ View(ExpoUI.ContextMenuPreview.self)
15
18
  View(ColorPickerView.self)
16
19
  View(DateTimePickerView.self)
17
20
  View(TextInputView.self)
18
21
  View(ProgressView.self)
19
22
  View(GaugeView.self)
23
+ View(ListView.self)
24
+ View(LabelView.self)
20
25
  }
21
26
  }
@@ -0,0 +1,24 @@
1
+ // Copyright 2025-present 650 Industries. All rights reserved.
2
+
3
+ import ExpoModulesCore
4
+ import SwiftUI
5
+
6
+ class LabelViewProps: ExpoSwiftUI.ViewProps {
7
+ @Field var title: String?
8
+ @Field var systemImage: String?
9
+ @Field var color: Color?
10
+ }
11
+
12
+ struct LabelView: ExpoSwiftUI.View {
13
+ @EnvironmentObject var props: LabelViewProps
14
+ @EnvironmentObject var shadowNodeProxy: ExpoSwiftUI.ShadowNodeProxy
15
+ var body: some View {
16
+ ExpoSwiftUI.AutoSizingStack(shadowNodeProxy: shadowNodeProxy, axis: .both) {
17
+ Label(
18
+ title: { Text(props.title ?? "") },
19
+ icon: { Image(systemName: props.systemImage ?? "").foregroundStyle(props.color ?? Color.accentColor) }
20
+ )
21
+ .fixedSize()
22
+ }
23
+ }
24
+ }