@expo/ui 56.0.2 → 56.0.4

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 (100) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +6 -0
  4. package/android/src/main/java/expo/modules/ui/MaskView.kt +56 -0
  5. package/build/community/masked-view/MaskedView.android.d.ts +8 -0
  6. package/build/community/masked-view/MaskedView.android.d.ts.map +1 -0
  7. package/build/community/masked-view/MaskedView.d.ts +10 -0
  8. package/build/community/masked-view/MaskedView.d.ts.map +1 -0
  9. package/build/community/masked-view/MaskedView.ios.d.ts +8 -0
  10. package/build/community/masked-view/MaskedView.ios.d.ts.map +1 -0
  11. package/build/community/masked-view/index.d.ts +4 -0
  12. package/build/community/masked-view/index.d.ts.map +1 -0
  13. package/build/community/masked-view/types.d.ts +19 -0
  14. package/build/community/masked-view/types.d.ts.map +1 -0
  15. package/build/community/picker/Picker.android.d.ts +10 -2
  16. package/build/community/picker/Picker.android.d.ts.map +1 -1
  17. package/build/community/picker/Picker.d.ts +10 -2
  18. package/build/community/picker/Picker.d.ts.map +1 -1
  19. package/build/community/picker/Picker.ios.d.ts +10 -2
  20. package/build/community/picker/Picker.ios.d.ts.map +1 -1
  21. package/build/community/picker/types.d.ts +12 -10
  22. package/build/community/picker/types.d.ts.map +1 -1
  23. package/build/jetpack-compose/modifiers/index.d.ts +1 -1
  24. package/build/universal/Button/index.d.ts.map +1 -1
  25. package/build/universal/Checkbox/index.d.ts.map +1 -1
  26. package/build/universal/Column/index.d.ts.map +1 -1
  27. package/build/universal/FieldGroup/FieldGroup.d.ts.map +1 -1
  28. package/build/universal/FieldGroup/FieldSection.d.ts.map +1 -1
  29. package/build/universal/Row/index.d.ts.map +1 -1
  30. package/build/universal/ScrollView/index.d.ts.map +1 -1
  31. package/build/universal/Slider/index.d.ts.map +1 -1
  32. package/build/universal/Spacer/index.d.ts.map +1 -1
  33. package/build/universal/Switch/index.d.ts.map +1 -1
  34. package/build/universal/Text/index.d.ts.map +1 -1
  35. package/expo-module.config.json +1 -1
  36. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.2/expo.modules.ui-56.0.2-sources.jar → 56.0.4/expo.modules.ui-56.0.4-sources.jar} +0 -0
  37. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4-sources.jar.md5 +1 -0
  38. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4-sources.jar.sha1 +1 -0
  39. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4-sources.jar.sha256 +1 -0
  40. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4-sources.jar.sha512 +1 -0
  41. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.aar +0 -0
  42. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.aar.md5 +1 -0
  43. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.aar.sha1 +1 -0
  44. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.aar.sha256 +1 -0
  45. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.aar.sha512 +1 -0
  46. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.2/expo.modules.ui-56.0.2.module → 56.0.4/expo.modules.ui-56.0.4.module} +23 -23
  47. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.module.md5 +1 -0
  48. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.module.sha1 +1 -0
  49. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.module.sha256 +1 -0
  50. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.module.sha512 +1 -0
  51. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.2/expo.modules.ui-56.0.2.pom → 56.0.4/expo.modules.ui-56.0.4.pom} +2 -2
  52. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.pom.md5 +1 -0
  53. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.pom.sha1 +1 -0
  54. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.pom.sha256 +1 -0
  55. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.4/expo.modules.ui-56.0.4.pom.sha512 +1 -0
  56. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
  57. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
  58. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
  59. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
  60. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
  61. package/package.json +8 -4
  62. package/src/community/masked-view/MaskedView.android.tsx +40 -0
  63. package/src/community/masked-view/MaskedView.ios.tsx +32 -0
  64. package/src/community/masked-view/MaskedView.tsx +34 -0
  65. package/src/community/masked-view/index.tsx +3 -0
  66. package/src/community/masked-view/types.ts +19 -0
  67. package/src/community/picker/Picker.android.tsx +12 -5
  68. package/src/community/picker/Picker.ios.tsx +9 -6
  69. package/src/community/picker/Picker.tsx +3 -4
  70. package/src/community/picker/types.tsx +24 -18
  71. package/src/jetpack-compose/modifiers/index.ts +1 -1
  72. package/src/ts-declarations/react-native-web.d.ts +112 -0
  73. package/src/universal/Button/index.tsx +37 -50
  74. package/src/universal/Checkbox/index.tsx +31 -18
  75. package/src/universal/Column/index.tsx +29 -18
  76. package/src/universal/FieldGroup/FieldGroup.tsx +24 -28
  77. package/src/universal/FieldGroup/FieldSection.tsx +62 -80
  78. package/src/universal/Row/index.tsx +37 -24
  79. package/src/universal/ScrollView/index.tsx +26 -15
  80. package/src/universal/Slider/index.tsx +21 -16
  81. package/src/universal/Spacer/index.tsx +18 -10
  82. package/src/universal/Switch/index.tsx +15 -10
  83. package/src/universal/Text/index.tsx +18 -37
  84. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2-sources.jar.md5 +0 -1
  85. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2-sources.jar.sha1 +0 -1
  86. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2-sources.jar.sha256 +0 -1
  87. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2-sources.jar.sha512 +0 -1
  88. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.aar +0 -0
  89. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.aar.md5 +0 -1
  90. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.aar.sha1 +0 -1
  91. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.aar.sha256 +0 -1
  92. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.aar.sha512 +0 -1
  93. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.module.md5 +0 -1
  94. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.module.sha1 +0 -1
  95. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.module.sha256 +0 -1
  96. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.module.sha512 +0 -1
  97. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.pom.md5 +0 -1
  98. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.pom.sha1 +0 -1
  99. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.pom.sha256 +0 -1
  100. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.2/expo.modules.ui-56.0.2.pom.sha512 +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/ui",
3
- "version": "56.0.2",
3
+ "version": "56.0.4",
4
4
  "description": "A collection of UI components",
5
5
  "sideEffects": [
6
6
  "*.fx.js"
@@ -43,6 +43,10 @@
43
43
  "types": "./build/community/picker/index.d.ts",
44
44
  "default": "./src/community/picker/index.tsx"
45
45
  },
46
+ "./community/masked-view": {
47
+ "types": "./build/community/masked-view/index.d.ts",
48
+ "default": "./src/community/masked-view/index.tsx"
49
+ },
46
50
  "./babel-plugin": {
47
51
  "types": "./plugin/babel-plugin.d.ts",
48
52
  "default": "./plugin/babel-plugin.js"
@@ -79,8 +83,8 @@
79
83
  "@types/react": "~19.2.0",
80
84
  "react-native-reanimated": "4.3.0",
81
85
  "react-native-worklets": "0.8.3",
82
- "expo": "56.0.0-preview.3",
83
- "expo-module-scripts": "56.0.1"
86
+ "expo": "56.0.0-preview.7",
87
+ "expo-module-scripts": "56.0.2"
84
88
  },
85
89
  "jest": {
86
90
  "preset": "expo-module-scripts"
@@ -108,7 +112,7 @@
108
112
  "optional": true
109
113
  }
110
114
  },
111
- "gitHead": "71ec800b2fa6133d22d69b70945132e22f1b7ae8",
115
+ "gitHead": "a30353e69ca0d72b9fac5830abc631feda1ba3ae",
112
116
  "scripts": {
113
117
  "build": "expo-module build",
114
118
  "clean": "expo-module clean",
@@ -0,0 +1,40 @@
1
+ import { requireNativeView } from 'expo';
2
+ import { StyleSheet, View } from 'react-native';
3
+
4
+ import type { MaskedViewProps } from './types';
5
+ import { Host } from '../../jetpack-compose/Host';
6
+ import { RNHostView } from '../../jetpack-compose/RNHostView';
7
+ import { Slot } from '../../jetpack-compose/SlotView';
8
+ import { fillMaxSize } from '../../jetpack-compose/modifiers';
9
+
10
+ const MaskNativeView: React.ComponentType<{
11
+ alignment?: 'topStart';
12
+ modifiers?: ReturnType<typeof fillMaxSize>[];
13
+ children?: React.ReactNode;
14
+ }> = requireNativeView('ExpoUI', 'MaskView');
15
+
16
+ /**
17
+ * Android implementation of `MaskedView`. Bridges arbitrary React Native children
18
+ * (and `maskElement`) into the Compose `MaskView` primitive via `RNHostView`.
19
+ */
20
+ export function MaskedView(props: MaskedViewProps) {
21
+ const { maskElement, children, style, ...viewProps } = props;
22
+ return (
23
+ <View {...viewProps} style={style}>
24
+ <Host style={StyleSheet.absoluteFill}>
25
+ <MaskNativeView alignment="topStart" modifiers={[fillMaxSize()]}>
26
+ <RNHostView modifiers={[fillMaxSize()]}>
27
+ <View style={[StyleSheet.absoluteFill, style]}>{children}</View>
28
+ </RNHostView>
29
+ <Slot slotName="content">
30
+ <RNHostView modifiers={[fillMaxSize()]}>
31
+ <View style={[StyleSheet.absoluteFill, style]}>{maskElement}</View>
32
+ </RNHostView>
33
+ </Slot>
34
+ </MaskNativeView>
35
+ </Host>
36
+ </View>
37
+ );
38
+ }
39
+
40
+ export default MaskedView;
@@ -0,0 +1,32 @@
1
+ import { StyleSheet, View } from 'react-native';
2
+
3
+ import type { MaskedViewProps } from './types';
4
+ import { Host } from '../../swift-ui/Host';
5
+ import { Mask } from '../../swift-ui/Mask';
6
+ import { RNHostView } from '../../swift-ui/RNHostView';
7
+
8
+ /**
9
+ * iOS implementation of `MaskedView`. Bridges arbitrary React Native children
10
+ * (and `maskElement`) into the SwiftUI `Mask` primitive via `RNHostView`.
11
+ */
12
+ export function MaskedView(props: MaskedViewProps) {
13
+ const { maskElement, children, style, ...viewProps } = props;
14
+ return (
15
+ <View {...viewProps} style={style}>
16
+ <Host style={StyleSheet.absoluteFill}>
17
+ <Mask alignment="topLeading">
18
+ <RNHostView>
19
+ <View style={[StyleSheet.absoluteFill, style]}>{children}</View>
20
+ </RNHostView>
21
+ <Mask.Content>
22
+ <RNHostView>
23
+ <View style={[StyleSheet.absoluteFill, style]}>{maskElement}</View>
24
+ </RNHostView>
25
+ </Mask.Content>
26
+ </Mask>
27
+ </Host>
28
+ </View>
29
+ );
30
+ }
31
+
32
+ export default MaskedView;
@@ -0,0 +1,34 @@
1
+ import { useEffect } from 'react';
2
+ import { View } from 'react-native';
3
+
4
+ import type { MaskedViewProps } from './types';
5
+
6
+ let warned = false;
7
+
8
+ /**
9
+ * Renders `children` with the alpha channel of `maskElement` applied as a mask:
10
+ * opaque pixels of `maskElement` reveal `children`, transparent pixels hide them.
11
+ *
12
+ * API-compatible with `@react-native-masked-view/masked-view`.
13
+ */
14
+ // This default file is used on platforms without a `.<platform>.tsx` override
15
+ // (notably web). It renders children unmasked and warns once.
16
+ export function MaskedView(props: MaskedViewProps) {
17
+ const { maskElement: _maskElement, children, style, ...rest } = props;
18
+ useEffect(() => {
19
+ if (!warned) {
20
+ warned = true;
21
+ console.warn(
22
+ '[@expo/ui/community/masked-view] MaskedView is not implemented on this platform. ' +
23
+ 'Children will render without a mask.'
24
+ );
25
+ }
26
+ }, []);
27
+ return (
28
+ <View {...rest} style={style}>
29
+ {children}
30
+ </View>
31
+ );
32
+ }
33
+
34
+ export default MaskedView;
@@ -0,0 +1,3 @@
1
+ export { MaskedView } from './MaskedView';
2
+ export { MaskedView as default } from './MaskedView';
3
+ export type { MaskedViewProps } from './types';
@@ -0,0 +1,19 @@
1
+ import type { ReactElement, ReactNode } from 'react';
2
+ import type { ViewProps } from 'react-native';
3
+
4
+ /**
5
+ * Drop-in props for `@react-native-masked-view/masked-view`'s `MaskedView`.
6
+ *
7
+ * @see https://github.com/callstack/masked-view
8
+ */
9
+ export interface MaskedViewProps extends ViewProps {
10
+ /**
11
+ * The element used as the mask. Only opaque pixels of `maskElement` make the
12
+ * masked content visible — transparent pixels hide it.
13
+ */
14
+ maskElement: ReactElement;
15
+ /**
16
+ * Content rendered behind the mask.
17
+ */
18
+ children?: ReactNode;
19
+ }
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import {
4
4
  extractPickerItems,
5
5
  PickerItem,
6
- type PickerWithItems,
6
+ type PickerItemProps,
7
7
  type PickerItemValue,
8
8
  type PickerProps,
9
9
  } from './types';
@@ -22,7 +22,7 @@ import { menuAnchor } from '../../jetpack-compose/modifiers';
22
22
  * A drop-in replacement for `@react-native-picker/picker` on Android.
23
23
  * Renders a Material 3 `ExposedDropdownMenuBox` wrapped in a Host.
24
24
  */
25
- function PickerImpl<T extends PickerItemValue>(props: PickerProps<T>) {
25
+ export function Picker<T extends PickerItemValue>(props: PickerProps<T>) {
26
26
  const { selectedValue, onValueChange, enabled, style, children, ref } = props;
27
27
  const items = extractPickerItems<T>(children);
28
28
  const [expanded, setExpanded] = React.useState(false);
@@ -52,7 +52,15 @@ function PickerImpl<T extends PickerItemValue>(props: PickerProps<T>) {
52
52
  setExpanded(false);
53
53
  }}>
54
54
  <DropdownMenuItem.Text>
55
- <Text>{item.label}</Text>
55
+ <Text
56
+ color={item.color}
57
+ style={{
58
+ fontFamily: item.fontFamily,
59
+ fontSize: item.fontSize,
60
+ background: item.backgroundColor,
61
+ }}>
62
+ {item.label}
63
+ </Text>
56
64
  </DropdownMenuItem.Text>
57
65
  </DropdownMenuItem>
58
66
  ))}
@@ -62,5 +70,4 @@ function PickerImpl<T extends PickerItemValue>(props: PickerProps<T>) {
62
70
  );
63
71
  }
64
72
 
65
- PickerImpl.displayName = 'Picker';
66
- export const Picker: PickerWithItems = Object.assign(PickerImpl, { Item: PickerItem });
73
+ Picker.Item = PickerItem as React.ComponentType<PickerItemProps>;
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import {
4
4
  extractPickerItems,
5
5
  PickerItem,
6
- type PickerWithItems,
6
+ type PickerItemProps,
7
7
  type PickerItemValue,
8
8
  type PickerProps,
9
9
  } from './types';
@@ -11,6 +11,7 @@ import { Host } from '../../swift-ui/Host';
11
11
  import { Picker as SwiftUIPicker } from '../../swift-ui/Picker';
12
12
  import { Text } from '../../swift-ui/Text';
13
13
  import {
14
+ backgroundOverlay,
14
15
  disabled as disabledModifier,
15
16
  fixedSize,
16
17
  foregroundStyle,
@@ -24,7 +25,7 @@ import { type ModifierConfig } from '../../types';
24
25
  * A drop-in replacement for `@react-native-picker/picker` on iOS.
25
26
  * Renders a SwiftUI wheel picker wrapped in a Host.
26
27
  */
27
- function PickerImpl<T extends PickerItemValue>(props: PickerProps<T>) {
28
+ export function Picker<T extends PickerItemValue>(props: PickerProps<T>) {
28
29
  const { selectedValue, onValueChange, enabled, style, children, ref } = props;
29
30
  const items = extractPickerItems<T>(children);
30
31
  const modifiers = [
@@ -55,8 +56,11 @@ function PickerImpl<T extends PickerItemValue>(props: PickerProps<T>) {
55
56
  if (item.color) {
56
57
  itemModifiers.push(foregroundStyle(item.color));
57
58
  }
58
- if (item.fontFamily) {
59
- itemModifiers.push(font({ family: item.fontFamily }));
59
+ if (item.fontFamily || item.fontSize != null) {
60
+ itemModifiers.push(font({ family: item.fontFamily, size: item.fontSize }));
61
+ }
62
+ if (item.backgroundColor) {
63
+ itemModifiers.push(backgroundOverlay({ color: item.backgroundColor }));
60
64
  }
61
65
  return (
62
66
  <Text key={String(item.value ?? index)} modifiers={itemModifiers}>
@@ -69,5 +73,4 @@ function PickerImpl<T extends PickerItemValue>(props: PickerProps<T>) {
69
73
  );
70
74
  }
71
75
 
72
- PickerImpl.displayName = 'Picker';
73
- export const Picker: PickerWithItems = Object.assign(PickerImpl, { Item: PickerItem });
76
+ Picker.Item = PickerItem as React.ComponentType<PickerItemProps>;
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import {
4
4
  extractPickerItems,
5
5
  PickerItem,
6
- type PickerWithItems,
6
+ type PickerItemProps,
7
7
  type PickerItemValue,
8
8
  type PickerProps,
9
9
  } from './types';
@@ -12,7 +12,7 @@ import {
12
12
  * A drop-in replacement for `@react-native-picker/picker` on web.
13
13
  * Renders a native `<select>` element.
14
14
  */
15
- function PickerImpl<T extends PickerItemValue>(props: PickerProps<T>) {
15
+ export function Picker<T extends PickerItemValue>(props: PickerProps<T>) {
16
16
  const { selectedValue, onValueChange, enabled, style, children, ref } = props;
17
17
  const items = extractPickerItems<T>(children);
18
18
  const selectRef = React.useRef<HTMLSelectElement>(null);
@@ -47,5 +47,4 @@ function PickerImpl<T extends PickerItemValue>(props: PickerProps<T>) {
47
47
  );
48
48
  }
49
49
 
50
- PickerImpl.displayName = 'Picker';
51
- export const Picker: PickerWithItems = Object.assign(PickerImpl, { Item: PickerItem });
50
+ Picker.Item = PickerItem as React.ComponentType<PickerItemProps>;
@@ -1,5 +1,5 @@
1
1
  import { Children, isValidElement, type Ref, type ReactNode, type ReactElement } from 'react';
2
- import type { StyleProp, ViewStyle } from 'react-native';
2
+ import { StyleSheet, type StyleProp, type TextStyle, type ViewStyle } from 'react-native';
3
3
 
4
4
  export type PickerItemValue = string | number | null;
5
5
 
@@ -7,7 +7,7 @@ export type PickerItemValue = string | number | null;
7
7
  * Props for the `Picker.Item` component.
8
8
  * Compatible with `@react-native-picker/picker`.
9
9
  */
10
- export type PickerItemProps<T extends PickerItemValue> = {
10
+ export type PickerItemProps<T extends PickerItemValue = PickerItemValue> = {
11
11
  /**
12
12
  * Display text for the item.
13
13
  */
@@ -17,15 +17,19 @@ export type PickerItemProps<T extends PickerItemValue> = {
17
17
  */
18
18
  value?: T;
19
19
  /**
20
- * Text color for the item.
21
- * @platform ios
20
+ * Text color for the item. Equivalent to setting `color` in the `style` prop.
22
21
  */
23
22
  color?: string;
24
23
  /**
25
- * Custom font family for the item.
26
- * @platform ios
24
+ * Custom font family for the item. Equivalent to setting `fontFamily` in the `style` prop.
27
25
  */
28
26
  fontFamily?: string;
27
+ /**
28
+ * Style applied to the item label. Only the following values take effect:
29
+ * `color`, `backgroundColor`, `fontFamily`, and `fontSize`. When also set
30
+ * via the top-level `color` or `fontFamily` props, values from `style` win.
31
+ */
32
+ style?: StyleProp<TextStyle>;
29
33
  /**
30
34
  * Whether the item is enabled.
31
35
  * @platform android
@@ -99,16 +103,13 @@ export type PickerRef = {
99
103
  blur: () => void;
100
104
  };
101
105
 
102
- export type PickerWithItems = {
103
- <T extends PickerItemValue>(props: PickerProps<T>): ReactElement | null;
104
- Item: typeof PickerItem;
105
- };
106
-
107
106
  export type ExtractedPickerItem<T extends PickerItemValue = PickerItemValue> = {
108
107
  label: string;
109
108
  value: T;
110
109
  color?: string;
110
+ backgroundColor?: string;
111
111
  fontFamily?: string;
112
+ fontSize?: number;
112
113
  enabled?: boolean;
113
114
  };
114
115
 
@@ -123,11 +124,16 @@ export function extractPickerItems<T extends PickerItemValue>(
123
124
  (child): child is ReactElement<PickerItemProps<T>> =>
124
125
  isValidElement(child) && child.type === PickerItem
125
126
  )
126
- .map(({ props: { label = '', value, color, fontFamily, enabled } }) => ({
127
- label,
128
- value: value as T,
129
- color,
130
- fontFamily,
131
- enabled,
132
- }));
127
+ .map(({ props: { label = '', value, color, fontFamily, style, enabled } }) => {
128
+ const flat = StyleSheet.flatten(style);
129
+ return {
130
+ label,
131
+ value: value as T,
132
+ color: (flat?.color as string | undefined) ?? color,
133
+ backgroundColor: flat?.backgroundColor as string | undefined,
134
+ fontFamily: flat?.fontFamily ?? fontFamily,
135
+ fontSize: flat?.fontSize,
136
+ enabled,
137
+ };
138
+ });
133
139
  }
@@ -142,7 +142,7 @@ export const offset = (x: number, y: number) => createModifier('offset', { x, y
142
142
 
143
143
  /**
144
144
  * Sets the background color.
145
- * @param color - Color string (hex, e.g., '#FF0000').
145
+ * @param color - A color string (hex, e.g., `'#FF0000'`).
146
146
  */
147
147
  export const background = (color: ColorValue) => createModifier('background', { color });
148
148
 
@@ -0,0 +1,112 @@
1
+ import * as ReactNative from 'react-native';
2
+
3
+ declare module 'react-native' {
4
+ export const unstable_createElement: <P>(
5
+ type: React.ElementType,
6
+ props?: P
7
+ ) => React.ReactElement<P>;
8
+
9
+ type DisplayValue = ReactNative.FlexStyle['display'] | 'inline-flex';
10
+
11
+ type WebRole =
12
+ | ReactNative.Role
13
+ /**
14
+ * Accessibility roles mapped to components
15
+ * @see https://github.com/necolas/react-native-web/blob/0.19.1/packages/react-native-web/src/modules/AccessibilityUtil/propsToAccessibilityComponent.js
16
+ */
17
+ | 'article' // <article />
18
+ | 'banner' // <header />
19
+ | 'blockquote' // <blockquote />
20
+ | 'button' // <button />
21
+ | 'code' // <code />
22
+ | 'complementary' // <aside />
23
+ | 'contentinfo' // <footer />
24
+ | 'deletion' // <del />
25
+ | 'emphasis' // <em />
26
+ | 'figure' // <figure />
27
+ | 'form' // <form />
28
+ | 'heading' // <h{1,6} />
29
+ | 'insertion' // <ins />
30
+ | 'label' // <label />
31
+ | 'list' // <ul />
32
+ | 'listitem' // <li />
33
+ | 'main' // <main />
34
+ | 'navigation' // <nav />
35
+ | 'paragraph' // <p />
36
+ | 'region' // <section />
37
+ | 'strong'; // <strong />
38
+
39
+ interface WebAccessibilityProps {
40
+ /**
41
+ * Additional accessibility props
42
+ */
43
+ tabIndex?: 0 | -1;
44
+
45
+ /**
46
+ * Aria props (additional, minus existants)
47
+ * @see https://necolas.github.io/react-native-web/docs/accessibility
48
+ * @see https://reactnative.dev/docs/accessibility#aria-valuemax
49
+ */
50
+ 'aria-activedescendant'?: string;
51
+ 'aria-atomic'?: boolean;
52
+ 'aria-autocomplete'?: string;
53
+ 'aria-colcount'?: number;
54
+ 'aria-colindex'?: number;
55
+ 'aria-colspan'?: number;
56
+ 'aria-controls'?: string;
57
+ 'aria-current'?: boolean | 'page' | 'step' | 'location' | 'date' | 'time';
58
+ 'aria-describedby'?: string;
59
+ 'aria-details'?: string;
60
+ 'aria-errormessage'?: string;
61
+ 'aria-flowto'?: string;
62
+ 'aria-haspopup'?: string;
63
+ 'aria-invalid'?: boolean;
64
+ 'aria-keyshortcuts'?: string;
65
+ 'aria-level'?: number;
66
+ 'aria-multiline'?: boolean;
67
+ 'aria-multiselectable'?: boolean;
68
+ 'aria-orientation'?: 'horizontal' | 'vertical';
69
+ 'aria-owns'?: string;
70
+ 'aria-placeholder'?: string;
71
+ 'aria-posinset'?: number;
72
+ 'aria-pressed'?: boolean;
73
+ 'aria-readonly'?: boolean;
74
+ 'aria-required'?: boolean;
75
+ 'aria-roledescription'?: string;
76
+ 'aria-rowcount'?: number;
77
+ 'aria-rowindex'?: number;
78
+ 'aria-rowspan'?: number;
79
+ 'aria-setsize'?: number;
80
+ 'aria-sort'?: 'ascending' | 'descending' | 'none' | 'other';
81
+ }
82
+
83
+ export interface PressableStateCallbackType {
84
+ readonly focused: boolean;
85
+ readonly hovered: boolean;
86
+ readonly pressed: boolean;
87
+ }
88
+
89
+ export interface ImageProps extends WebAccessibilityProps {
90
+ role?: WebRole;
91
+ }
92
+
93
+ export interface TextProps extends WebAccessibilityProps {
94
+ role?: WebRole;
95
+ }
96
+
97
+ export interface ViewProps extends WebAccessibilityProps {
98
+ role?: WebRole;
99
+ }
100
+
101
+ export interface ImageStyle {
102
+ display?: DisplayValue;
103
+ }
104
+
105
+ export interface TextStyle {
106
+ display?: DisplayValue;
107
+ }
108
+
109
+ export interface ViewStyle {
110
+ display?: DisplayValue;
111
+ }
112
+ }
@@ -1,48 +1,40 @@
1
- import { useState } from 'react';
2
- import { Pressable, Text, type ViewStyle } from 'react-native';
1
+ import { Pressable, StyleSheet, Text, type TextStyle, type ViewStyle } from 'react-native';
3
2
 
4
3
  import type { ButtonProps, ButtonVariant } from './types';
5
4
  import { useUniversalLifecycle } from '../hooks';
6
5
 
7
- const variantStyles: Record<ButtonVariant, ViewStyle> = {
8
- filled: {
9
- backgroundColor: '#007AFF',
6
+ const styles = StyleSheet.create({
7
+ button: {
10
8
  paddingHorizontal: 16,
11
9
  paddingVertical: 10,
12
10
  borderRadius: 8,
11
+ userSelect: 'none',
13
12
  },
13
+ disabled: { opacity: 0.5 },
14
+ hidden: { display: 'none' },
15
+ label: { textAlign: 'center' },
16
+ });
17
+
18
+ const variantStyles = StyleSheet.create({
19
+ filled: { backgroundColor: '#007AFF' },
14
20
  outlined: {
15
- backgroundColor: 'transparent',
16
- paddingHorizontal: 16,
17
- paddingVertical: 10,
18
- borderRadius: 8,
19
- borderWidth: 1,
20
21
  borderColor: '#007AFF',
22
+ borderWidth: 1,
21
23
  },
22
- text: {
23
- backgroundColor: 'transparent',
24
- paddingHorizontal: 16,
25
- paddingVertical: 10,
26
- },
27
- };
24
+ text: {},
25
+ } satisfies Record<ButtonVariant, ViewStyle>);
28
26
 
29
- const variantHoverStyles: Record<ButtonVariant, ViewStyle> = {
30
- filled: {
31
- backgroundColor: '#0066DB',
32
- },
33
- outlined: {
34
- backgroundColor: 'rgba(0, 122, 255, 0.08)',
35
- },
36
- text: {
37
- backgroundColor: 'rgba(0, 122, 255, 0.08)',
38
- },
39
- };
27
+ const variantHoverStyles = StyleSheet.create({
28
+ filled: { backgroundColor: '#0066DB' },
29
+ outlined: { backgroundColor: 'rgba(0, 122, 255, 0.08)' },
30
+ text: { backgroundColor: 'rgba(0, 122, 255, 0.08)' },
31
+ } satisfies Record<ButtonVariant, ViewStyle>);
40
32
 
41
- const variantTextColors: Record<ButtonVariant, string> = {
42
- filled: '#FFFFFF',
43
- outlined: '#007AFF',
44
- text: '#007AFF',
45
- };
33
+ const variantLabelStyles = StyleSheet.create({
34
+ filled: { color: '#FFFFFF' },
35
+ outlined: { color: '#007AFF' },
36
+ text: { color: '#007AFF' },
37
+ } satisfies Record<ButtonVariant, TextStyle>);
46
38
 
47
39
  /**
48
40
  * A pressable button that supports multiple visual variants.
@@ -55,32 +47,27 @@ export function Button({
55
47
  style,
56
48
  onAppear,
57
49
  onDisappear,
58
- disabled,
59
- hidden,
50
+ disabled = false,
51
+ hidden = false,
60
52
  testID,
61
53
  }: ButtonProps) {
62
54
  useUniversalLifecycle(onAppear, onDisappear);
63
- const [hovered, setHovered] = useState(false);
64
-
65
- const pressableStyle: ViewStyle = {
66
- ...variantStyles[variant],
67
- ...style,
68
- ...(hovered && !disabled ? variantHoverStyles[variant] : undefined),
69
- ...(hidden ? { display: 'none' } : undefined),
70
- ...(disabled ? { opacity: 0.5 } : undefined),
71
- };
72
55
 
73
56
  return (
74
57
  <Pressable
75
- style={pressableStyle}
58
+ role="button"
76
59
  onPress={onPress}
77
- onHoverIn={() => setHovered(true)}
78
- onHoverOut={() => setHovered(false)}
79
60
  disabled={disabled}
80
- testID={testID}>
81
- {children ?? (
82
- <Text style={{ color: variantTextColors[variant], textAlign: 'center' }}>{label}</Text>
83
- )}
61
+ testID={testID}
62
+ style={({ hovered }) => [
63
+ styles.button,
64
+ variantStyles[variant],
65
+ style,
66
+ hovered && !disabled && variantHoverStyles[variant],
67
+ hidden && styles.hidden,
68
+ disabled && styles.disabled,
69
+ ]}>
70
+ {children ?? <Text style={[styles.label, variantLabelStyles[variant]]}>{label}</Text>}
84
71
  </Pressable>
85
72
  );
86
73
  }
@@ -1,33 +1,46 @@
1
+ import type { ComponentProps } from 'react';
2
+ import { StyleSheet, Text, unstable_createElement, View, type ViewProps } from 'react-native';
3
+
1
4
  import type { CheckboxProps } from './types';
2
5
 
6
+ const styles = StyleSheet.create({
7
+ label: {
8
+ flexDirection: 'row',
9
+ display: 'inline-flex',
10
+ alignItems: 'center',
11
+ gap: 8,
12
+ cursor: 'pointer',
13
+ },
14
+ disabled: {
15
+ opacity: 0.5,
16
+ cursor: 'auto',
17
+ },
18
+ cursorInherit: {
19
+ // @ts-expect-error
20
+ cursor: 'inherit',
21
+ },
22
+ });
23
+
24
+ const NativeCheckbox = (
25
+ props: Omit<ComponentProps<'input'>, 'style' | 'type'> & { style?: ViewProps['style'] }
26
+ ) => unstable_createElement('input', { ...props, type: 'checkbox' });
27
+
3
28
  /**
4
29
  * A toggle control that represents a checked or unchecked state.
5
30
  */
6
- export function Checkbox({ value, onValueChange, label, disabled, testID }: CheckboxProps) {
31
+ export function Checkbox({ value, onValueChange, label, disabled = false, testID }: CheckboxProps) {
7
32
  return (
8
- <label style={{ ...labelStyle, ...(disabled ? disabledStyle : undefined) }}>
9
- <input
10
- type="checkbox"
33
+ <View role="label" aria-disabled={disabled} style={[styles.label, disabled && styles.disabled]}>
34
+ <NativeCheckbox
11
35
  checked={value}
12
36
  onChange={(e) => onValueChange(e.target.checked)}
13
37
  disabled={disabled}
14
38
  data-testid={testID}
39
+ style={styles.cursorInherit}
15
40
  />
16
- {label != null && <span>{label}</span>}
17
- </label>
41
+ {label != null && <Text>{label}</Text>}
42
+ </View>
18
43
  );
19
44
  }
20
45
 
21
- const labelStyle: React.CSSProperties = {
22
- display: 'inline-flex',
23
- alignItems: 'center',
24
- gap: 8,
25
- cursor: 'pointer',
26
- };
27
-
28
- const disabledStyle: React.CSSProperties = {
29
- opacity: 0.5,
30
- cursor: 'default',
31
- };
32
-
33
46
  export * from './types';