react-native-platform-components 1.3.0 → 1.4.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.
Files changed (181) hide show
  1. package/README.md +24 -30
  2. package/android/src/main/java/com/platformcomponents/PCButtonGroupView.kt +498 -0
  3. package/android/src/main/java/com/platformcomponents/PCButtonGroupViewManager.kt +191 -0
  4. package/android/src/main/java/com/platformcomponents/PCButtonSupport.kt +127 -0
  5. package/android/src/main/java/com/platformcomponents/PCButtonView.kt +345 -0
  6. package/android/src/main/java/com/platformcomponents/PCButtonViewManager.kt +126 -0
  7. package/android/src/main/java/com/platformcomponents/PCExpressive.kt +107 -0
  8. package/android/src/main/java/com/platformcomponents/PCFloatingToolbarView.kt +115 -0
  9. package/android/src/main/java/com/platformcomponents/PCFloatingToolbarViewManager.kt +65 -0
  10. package/android/src/main/java/com/platformcomponents/PCSegmentedControlView.kt +11 -3
  11. package/android/src/main/java/com/platformcomponents/PCSegmentedControlViewManager.kt +6 -1
  12. package/android/src/main/java/com/platformcomponents/PCTextFieldView.kt +1018 -0
  13. package/android/src/main/java/com/platformcomponents/PCTextFieldViewManager.kt +248 -0
  14. package/android/src/main/java/com/platformcomponents/PlatformComponentsPackage.kt +4 -0
  15. package/android/src/main/res/values/attrs.xml +13 -0
  16. package/android/src/main/res/values/styles.xml +183 -0
  17. package/ios/PCButton.h +10 -0
  18. package/ios/PCButton.mm +196 -0
  19. package/ios/PCButtonGroup.h +10 -0
  20. package/ios/PCButtonGroup.mm +256 -0
  21. package/ios/PCButtonGroupView.swift +254 -0
  22. package/ios/PCButtonSupport.swift +141 -0
  23. package/ios/PCButtonView.swift +155 -0
  24. package/ios/PCFloatingToolbar.h +10 -0
  25. package/ios/PCFloatingToolbar.mm +76 -0
  26. package/ios/PCFloatingToolbarView.swift +95 -0
  27. package/ios/PCTextField.h +10 -0
  28. package/ios/PCTextField.mm +391 -0
  29. package/ios/PCTextFieldView.swift +1012 -0
  30. package/lib/commonjs/Button.js +77 -0
  31. package/lib/commonjs/Button.js.map +1 -0
  32. package/lib/commonjs/ButtonGroup.js +99 -0
  33. package/lib/commonjs/ButtonGroup.js.map +1 -0
  34. package/lib/commonjs/ButtonGroupNativeComponent.ts +121 -0
  35. package/lib/commonjs/ButtonNativeComponent.ts +76 -0
  36. package/lib/commonjs/FloatingToolbar.js +69 -0
  37. package/lib/commonjs/FloatingToolbar.js.map +1 -0
  38. package/lib/commonjs/FloatingToolbarNativeComponent.ts +31 -0
  39. package/lib/commonjs/SegmentedControl.js +11 -85
  40. package/lib/commonjs/SegmentedControl.js.map +1 -1
  41. package/lib/commonjs/SegmentedControlNativeComponent.ts +3 -0
  42. package/lib/commonjs/TextField.js +195 -0
  43. package/lib/commonjs/TextField.js.map +1 -0
  44. package/lib/commonjs/TextFieldNativeComponent.ts +218 -0
  45. package/lib/commonjs/focusRegistry.js +78 -0
  46. package/lib/commonjs/focusRegistry.js.map +1 -0
  47. package/lib/commonjs/icons.js +92 -0
  48. package/lib/commonjs/icons.js.map +1 -0
  49. package/lib/commonjs/index.js +66 -0
  50. package/lib/commonjs/index.js.map +1 -1
  51. package/lib/commonjs/index.web.js +32 -2
  52. package/lib/commonjs/index.web.js.map +1 -1
  53. package/lib/commonjs/labelStyle.js +27 -0
  54. package/lib/commonjs/labelStyle.js.map +1 -0
  55. package/lib/commonjs/reactNativeInternals.d.js +2 -0
  56. package/lib/commonjs/reactNativeInternals.d.js.map +1 -0
  57. package/lib/module/Button.js +70 -0
  58. package/lib/module/Button.js.map +1 -0
  59. package/lib/module/ButtonGroup.js +92 -0
  60. package/lib/module/ButtonGroup.js.map +1 -0
  61. package/lib/module/ButtonGroupNativeComponent.ts +121 -0
  62. package/lib/module/ButtonNativeComponent.ts +76 -0
  63. package/lib/module/FloatingToolbar.js +62 -0
  64. package/lib/module/FloatingToolbar.js.map +1 -0
  65. package/lib/module/FloatingToolbarNativeComponent.ts +31 -0
  66. package/lib/module/SegmentedControl.js +12 -87
  67. package/lib/module/SegmentedControl.js.map +1 -1
  68. package/lib/module/SegmentedControlNativeComponent.ts +3 -0
  69. package/lib/module/TextField.js +189 -0
  70. package/lib/module/TextField.js.map +1 -0
  71. package/lib/module/TextFieldNativeComponent.ts +218 -0
  72. package/lib/module/focusRegistry.js +74 -0
  73. package/lib/module/focusRegistry.js.map +1 -0
  74. package/lib/module/icons.js +87 -0
  75. package/lib/module/icons.js.map +1 -0
  76. package/lib/module/index.js +6 -0
  77. package/lib/module/index.js.map +1 -1
  78. package/lib/module/index.web.js +28 -0
  79. package/lib/module/index.web.js.map +1 -1
  80. package/lib/module/labelStyle.js +23 -0
  81. package/lib/module/labelStyle.js.map +1 -0
  82. package/lib/module/reactNativeInternals.d.js +2 -0
  83. package/lib/module/reactNativeInternals.d.js.map +1 -0
  84. package/lib/typescript/commonjs/docs/sidebars.d.ts.map +1 -1
  85. package/lib/typescript/commonjs/src/Button.d.ts +76 -0
  86. package/lib/typescript/commonjs/src/Button.d.ts.map +1 -0
  87. package/lib/typescript/commonjs/src/ButtonGroup.d.ts +102 -0
  88. package/lib/typescript/commonjs/src/ButtonGroup.d.ts.map +1 -0
  89. package/lib/typescript/commonjs/src/ButtonGroupNativeComponent.d.ts +93 -0
  90. package/lib/typescript/commonjs/src/ButtonGroupNativeComponent.d.ts.map +1 -0
  91. package/lib/typescript/commonjs/src/ButtonNativeComponent.d.ts +57 -0
  92. package/lib/typescript/commonjs/src/ButtonNativeComponent.d.ts.map +1 -0
  93. package/lib/typescript/commonjs/src/FloatingToolbar.d.ts +45 -0
  94. package/lib/typescript/commonjs/src/FloatingToolbar.d.ts.map +1 -0
  95. package/lib/typescript/commonjs/src/FloatingToolbarNativeComponent.d.ts +24 -0
  96. package/lib/typescript/commonjs/src/FloatingToolbarNativeComponent.d.ts.map +1 -0
  97. package/lib/typescript/commonjs/src/SegmentedControl.d.ts +17 -46
  98. package/lib/typescript/commonjs/src/SegmentedControl.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts +2 -0
  100. package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/src/TextField.d.ts +174 -0
  102. package/lib/typescript/commonjs/src/TextField.d.ts.map +1 -0
  103. package/lib/typescript/commonjs/src/TextFieldNativeComponent.d.ts +162 -0
  104. package/lib/typescript/commonjs/src/TextFieldNativeComponent.d.ts.map +1 -0
  105. package/lib/typescript/commonjs/src/focusRegistry.d.ts +13 -0
  106. package/lib/typescript/commonjs/src/focusRegistry.d.ts.map +1 -0
  107. package/lib/typescript/commonjs/src/icons.d.ts +52 -0
  108. package/lib/typescript/commonjs/src/icons.d.ts.map +1 -0
  109. package/lib/typescript/commonjs/src/index.d.ts +6 -0
  110. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/src/index.web.d.ts +121 -0
  112. package/lib/typescript/commonjs/src/index.web.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/src/labelStyle.d.ts +20 -0
  114. package/lib/typescript/commonjs/src/labelStyle.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/src/sharedTypes.d.ts +6 -1
  116. package/lib/typescript/commonjs/src/sharedTypes.d.ts.map +1 -1
  117. package/lib/typescript/module/docs/sidebars.d.ts.map +1 -1
  118. package/lib/typescript/module/src/Button.d.ts +76 -0
  119. package/lib/typescript/module/src/Button.d.ts.map +1 -0
  120. package/lib/typescript/module/src/ButtonGroup.d.ts +102 -0
  121. package/lib/typescript/module/src/ButtonGroup.d.ts.map +1 -0
  122. package/lib/typescript/module/src/ButtonGroupNativeComponent.d.ts +93 -0
  123. package/lib/typescript/module/src/ButtonGroupNativeComponent.d.ts.map +1 -0
  124. package/lib/typescript/module/src/ButtonNativeComponent.d.ts +57 -0
  125. package/lib/typescript/module/src/ButtonNativeComponent.d.ts.map +1 -0
  126. package/lib/typescript/module/src/FloatingToolbar.d.ts +45 -0
  127. package/lib/typescript/module/src/FloatingToolbar.d.ts.map +1 -0
  128. package/lib/typescript/module/src/FloatingToolbarNativeComponent.d.ts +24 -0
  129. package/lib/typescript/module/src/FloatingToolbarNativeComponent.d.ts.map +1 -0
  130. package/lib/typescript/module/src/SegmentedControl.d.ts +17 -46
  131. package/lib/typescript/module/src/SegmentedControl.d.ts.map +1 -1
  132. package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts +2 -0
  133. package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts.map +1 -1
  134. package/lib/typescript/module/src/TextField.d.ts +174 -0
  135. package/lib/typescript/module/src/TextField.d.ts.map +1 -0
  136. package/lib/typescript/module/src/TextFieldNativeComponent.d.ts +162 -0
  137. package/lib/typescript/module/src/TextFieldNativeComponent.d.ts.map +1 -0
  138. package/lib/typescript/module/src/focusRegistry.d.ts +13 -0
  139. package/lib/typescript/module/src/focusRegistry.d.ts.map +1 -0
  140. package/lib/typescript/module/src/icons.d.ts +52 -0
  141. package/lib/typescript/module/src/icons.d.ts.map +1 -0
  142. package/lib/typescript/module/src/index.d.ts +6 -0
  143. package/lib/typescript/module/src/index.d.ts.map +1 -1
  144. package/lib/typescript/module/src/index.web.d.ts +121 -0
  145. package/lib/typescript/module/src/index.web.d.ts.map +1 -1
  146. package/lib/typescript/module/src/labelStyle.d.ts +20 -0
  147. package/lib/typescript/module/src/labelStyle.d.ts.map +1 -0
  148. package/lib/typescript/module/src/sharedTypes.d.ts +6 -1
  149. package/lib/typescript/module/src/sharedTypes.d.ts.map +1 -1
  150. package/package.json +25 -4
  151. package/react-native.config.js +3 -0
  152. package/shared/PCButtonComponentDescriptors-custom.h +22 -0
  153. package/shared/PCButtonGroupComponentDescriptors-custom.h +22 -0
  154. package/shared/PCButtonGroupShadowNode-custom.cpp +48 -0
  155. package/shared/PCButtonGroupShadowNode-custom.h +55 -0
  156. package/shared/PCButtonGroupState-custom.h +61 -0
  157. package/shared/PCButtonShadowNode-custom.cpp +48 -0
  158. package/shared/PCButtonShadowNode-custom.h +55 -0
  159. package/shared/PCButtonState-custom.h +61 -0
  160. package/shared/PCTextFieldComponentDescriptors-custom.h +22 -0
  161. package/shared/PCTextFieldShadowNode-custom.cpp +47 -0
  162. package/shared/PCTextFieldShadowNode-custom.h +55 -0
  163. package/shared/PCTextFieldState-custom.h +61 -0
  164. package/shared/react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h +3 -0
  165. package/src/Button.tsx +144 -0
  166. package/src/ButtonGroup.tsx +233 -0
  167. package/src/ButtonGroupNativeComponent.ts +121 -0
  168. package/src/ButtonNativeComponent.ts +76 -0
  169. package/src/FloatingToolbar.tsx +113 -0
  170. package/src/FloatingToolbarNativeComponent.ts +31 -0
  171. package/src/SegmentedControl.tsx +26 -118
  172. package/src/SegmentedControlNativeComponent.ts +3 -0
  173. package/src/TextField.tsx +494 -0
  174. package/src/TextFieldNativeComponent.ts +218 -0
  175. package/src/focusRegistry.ts +89 -0
  176. package/src/icons.ts +116 -0
  177. package/src/index.tsx +6 -0
  178. package/src/index.web.tsx +194 -0
  179. package/src/labelStyle.ts +36 -0
  180. package/src/reactNativeInternals.d.ts +22 -0
  181. package/src/sharedTypes.ts +7 -1
@@ -0,0 +1,121 @@
1
+ // ButtonGroupNativeComponent.ts
2
+ import type { ColorValue, HostComponent, ViewProps } from 'react-native';
3
+ import { codegenNativeComponent } from 'react-native';
4
+ import type { BubblingEventHandler, Double, Int32 } from './codegenTypes';
5
+
6
+ /**
7
+ * A single button in the group. Icons are pre-resolved on the JS side (see
8
+ * icons.ts) so native only deals with flat strings.
9
+ */
10
+ export type ButtonGroupButton = Readonly<{
11
+ label: string; // Empty for an icon-only button
12
+ value: string;
13
+ disabled: string; // 'enabled' | 'disabled'
14
+ iconType: string; // '' | 'sfSymbol' | 'drawable' | 'image'
15
+ iconName: string; // SF Symbol (iOS) or drawable resource name (Android)
16
+ iconUri: string; // Resolved image URI when iconType === 'image'
17
+ iconScale: Double; // Resolved image scale when iconType === 'image'
18
+ iconTinted: string; // 'true' | 'false' — draw the image as a tinted template
19
+ accessibilityLabel: string; // Screen-reader label, empty = use label
20
+ }>;
21
+
22
+ /**
23
+ * Event emitted when a button is pressed.
24
+ */
25
+ export type ButtonGroupPressEvent = Readonly<{
26
+ index: Int32;
27
+ value: string;
28
+ }>;
29
+
30
+ /**
31
+ * Event emitted when the selection changes (single / multiple selection).
32
+ */
33
+ export type ButtonGroupSelectionEvent = Readonly<{
34
+ /** Values of the selected buttons, in button order. */
35
+ // Codegen only understands the `T[]` array form in event payloads.
36
+ values: string[];
37
+ }>;
38
+
39
+ /**
40
+ * Label font. Empty strings / 0 mean "platform default".
41
+ */
42
+ export type ButtonGroupLabelStyleProps = Readonly<{
43
+ fontFamily?: string;
44
+ fontSize?: Double;
45
+ fontWeight?: string; // 'normal' | 'bold' | '100'..'900'
46
+ fontStyle?: string; // 'normal' | 'italic'
47
+ }>;
48
+
49
+ /**
50
+ * Android-specific configuration.
51
+ */
52
+ export type ButtonGroupAndroidProps = Readonly<{
53
+ /** What happens to buttons that don't fit: 'none' | 'menu' | 'wrap' */
54
+ overflow?: string;
55
+
56
+ /** Material style: 'm3' | 'expressive' */
57
+ material?: string;
58
+ }>;
59
+
60
+ export interface ButtonGroupNativeProps extends ViewProps {
61
+ /** Buttons to display. */
62
+ buttons: ReadonlyArray<ButtonGroupButton>;
63
+
64
+ /** 'filled' | 'tonal' | 'outlined' | 'text' | 'elevated' */
65
+ variant?: string;
66
+
67
+ /** 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' */
68
+ size?: string;
69
+
70
+ /** '' (platform default) | 'round' | 'square' */
71
+ shape?: string;
72
+
73
+ /** 'true' | 'false': connected group (shared outline, small inner corners) */
74
+ connected?: string;
75
+
76
+ /** Gap between buttons in points; negative means the platform default. */
77
+ spacing?: Double;
78
+
79
+ /** 'none' | 'single' | 'multiple' */
80
+ selection?: string;
81
+
82
+ /** Values of the selected buttons (single / multiple selection). */
83
+ selectedValues?: ReadonlyArray<string>;
84
+
85
+ /** 'true' | 'false': whether at least one button must stay selected */
86
+ selectionRequired?: string;
87
+
88
+ /** 'enabled' | 'disabled' */
89
+ interactivity?: string;
90
+
91
+ /** Container (background) color of the buttons. */
92
+ color?: ColorValue;
93
+
94
+ /** Label and icon color of the buttons. */
95
+ foregroundColor?: ColorValue;
96
+
97
+ /** Android: ripple color shown while pressing a button. */
98
+ androidRippleColor?: ColorValue;
99
+
100
+ /** Android: outline color (outlined variant). */
101
+ androidStrokeColor?: ColorValue;
102
+
103
+ /** Label font. */
104
+ labelStyle?: ButtonGroupLabelStyleProps;
105
+
106
+ /** Fired when a button is pressed. */
107
+ onButtonPress?: BubblingEventHandler<ButtonGroupPressEvent>;
108
+
109
+ /**
110
+ * Fired when the selection changes. Not `onSelectionChange`: React Native's
111
+ * TextInput registers `topSelectionChange` as a direct event, and an event
112
+ * name can't be both direct and bubbling.
113
+ */
114
+ onGroupSelectionChange?: BubblingEventHandler<ButtonGroupSelectionEvent>;
115
+
116
+ android?: ButtonGroupAndroidProps;
117
+ }
118
+
119
+ export default codegenNativeComponent<ButtonGroupNativeProps>(
120
+ 'PCButtonGroup'
121
+ ) as HostComponent<ButtonGroupNativeProps>;
@@ -0,0 +1,76 @@
1
+ // ButtonNativeComponent.ts
2
+ import type { ColorValue, HostComponent, ViewProps } from 'react-native';
3
+ import { codegenNativeComponent } from 'react-native';
4
+ import type { BubblingEventHandler, Double } from './codegenTypes';
5
+
6
+ /**
7
+ * Icon fields, pre-resolved on the JS side (see icons.ts).
8
+ */
9
+ export type ButtonIconProps = Readonly<{
10
+ iconType: string; // '' | 'sfSymbol' | 'drawable' | 'image'
11
+ iconName: string; // SF Symbol (iOS) or drawable resource name (Android)
12
+ iconUri: string; // Resolved image URI when iconType === 'image'
13
+ iconScale: Double; // Resolved image scale when iconType === 'image'
14
+ iconTinted: string; // 'true' | 'false' — draw the image as a tinted template
15
+ }>;
16
+
17
+ /**
18
+ * Label font. Empty strings / 0 mean "platform default".
19
+ */
20
+ export type ButtonLabelStyleProps = Readonly<{
21
+ fontFamily?: string;
22
+ fontSize?: Double;
23
+ fontWeight?: string; // 'normal' | 'bold' | '100'..'900'
24
+ fontStyle?: string; // 'normal' | 'italic'
25
+ }>;
26
+
27
+ export interface ButtonNativeProps extends ViewProps {
28
+ /** Button text. Empty for an icon-only button. */
29
+ label?: string;
30
+
31
+ /** Icon shown before the label, or alone when there is no label. */
32
+ icon?: ButtonIconProps;
33
+
34
+ /** 'filled' | 'tonal' | 'outlined' | 'text' | 'elevated' */
35
+ variant?: string;
36
+
37
+ /** 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' */
38
+ size?: string;
39
+
40
+ /** '' (platform default) | 'round' | 'square' */
41
+ shape?: string;
42
+
43
+ /** 'enabled' | 'disabled' */
44
+ interactivity?: string;
45
+
46
+ /** Container (background) color. */
47
+ color?: ColorValue;
48
+
49
+ /** Label and icon color. */
50
+ foregroundColor?: ColorValue;
51
+
52
+ /** Android: ripple color shown while pressing. */
53
+ androidRippleColor?: ColorValue;
54
+
55
+ /** Android: outline color (outlined variant). */
56
+ androidStrokeColor?: ColorValue;
57
+
58
+ /** Android: Material style, 'm3' | 'expressive' */
59
+ androidMaterial?: string;
60
+
61
+ /** Label font. */
62
+ labelStyle?: ButtonLabelStyleProps;
63
+
64
+ /**
65
+ * Screen-reader label, applied to the native button rather than the host
66
+ * view. Empty means "use the label".
67
+ */
68
+ spokenLabel?: string;
69
+
70
+ /** Fired when the button is pressed. */
71
+ onButtonPress?: BubblingEventHandler<Readonly<{}>>;
72
+ }
73
+
74
+ export default codegenNativeComponent<ButtonNativeProps>(
75
+ 'PCButton'
76
+ ) as HostComponent<ButtonNativeProps>;
@@ -0,0 +1,113 @@
1
+ // FloatingToolbar.tsx
2
+ import React, { useMemo } from 'react';
3
+ import {
4
+ Platform,
5
+ StyleSheet,
6
+ type ColorValue,
7
+ type StyleProp,
8
+ type ViewProps,
9
+ type ViewStyle,
10
+ } from 'react-native';
11
+
12
+ import NativeFloatingToolbar from './FloatingToolbarNativeComponent';
13
+
14
+ /** Glass effect style of the toolbar on iOS 26. */
15
+ export type FloatingToolbarIOSEffect = 'regular' | 'clear';
16
+
17
+ /** Material 3 color variant of the toolbar on Android. */
18
+ export type FloatingToolbarAndroidVariant = 'standard' | 'vibrant';
19
+
20
+ export interface FloatingToolbarProps extends ViewProps {
21
+ /**
22
+ * Layout direction of the children. Default: `'horizontal'`.
23
+ */
24
+ orientation?: 'horizontal' | 'vertical';
25
+
26
+ /**
27
+ * Container color. Default: the platform's toolbar material (Material 3
28
+ * `surfaceContainer` or `primaryContainer` on Android, Liquid Glass or a
29
+ * blur on iOS). Use this rather than `style.backgroundColor`, which would
30
+ * replace the native shape.
31
+ */
32
+ color?: ColorValue;
33
+
34
+ /**
35
+ * iOS-specific configuration
36
+ */
37
+ ios?: {
38
+ /**
39
+ * Glass effect style on iOS 26. Older versions use a blur.
40
+ * Default: `'regular'`.
41
+ */
42
+ effect?: FloatingToolbarIOSEffect;
43
+ };
44
+
45
+ /**
46
+ * Android-specific configuration
47
+ */
48
+ android?: {
49
+ /**
50
+ * Material 3 color variant: `'standard'` (surface) or `'vibrant'`
51
+ * (primary container). Default: `'standard'`.
52
+ */
53
+ variant?: FloatingToolbarAndroidVariant;
54
+ };
55
+
56
+ /** The toolbar's actions, usually `Button`s. */
57
+ children?: React.ReactNode;
58
+
59
+ /** Test identifier */
60
+ testID?: string;
61
+ }
62
+
63
+ export function FloatingToolbar(
64
+ props: FloatingToolbarProps
65
+ ): React.ReactElement {
66
+ const { style, orientation, color, ios, android, children, ...viewProps } =
67
+ props;
68
+
69
+ const nativeIos = useMemo(() => {
70
+ if (Platform.OS !== 'ios' || !ios) return undefined;
71
+ return { effect: ios.effect ?? 'regular' };
72
+ }, [ios]);
73
+
74
+ const nativeAndroid = useMemo(() => {
75
+ if (Platform.OS !== 'android' || !android) return undefined;
76
+ return { variant: android.variant ?? 'standard' };
77
+ }, [android]);
78
+
79
+ const mergedStyle = useMemo(
80
+ (): StyleProp<ViewStyle> => [
81
+ styles.toolbar,
82
+ orientation === 'vertical' ? styles.vertical : styles.horizontal,
83
+ style,
84
+ ],
85
+ [orientation, style]
86
+ );
87
+
88
+ return (
89
+ <NativeFloatingToolbar
90
+ style={mergedStyle}
91
+ color={color}
92
+ ios={nativeIos}
93
+ android={nativeAndroid}
94
+ {...viewProps}
95
+ >
96
+ {children}
97
+ </NativeFloatingToolbar>
98
+ );
99
+ }
100
+
101
+ // Material 3 floating toolbar: 8dp container padding, 4dp between actions.
102
+ // The capsule shape comes from native.
103
+ const styles = StyleSheet.create({
104
+ toolbar: {
105
+ alignSelf: 'center',
106
+ alignItems: 'center',
107
+ justifyContent: 'center',
108
+ padding: 8,
109
+ gap: 4,
110
+ },
111
+ horizontal: { flexDirection: 'row' },
112
+ vertical: { flexDirection: 'column' },
113
+ });
@@ -0,0 +1,31 @@
1
+ // FloatingToolbarNativeComponent.ts
2
+ import type { ColorValue, HostComponent, ViewProps } from 'react-native';
3
+ import { codegenNativeComponent } from 'react-native';
4
+
5
+ /**
6
+ * iOS-specific configuration.
7
+ */
8
+ export type FloatingToolbarIOSProps = Readonly<{
9
+ /** Glass effect style on iOS 26: 'regular' | 'clear' */
10
+ effect?: string;
11
+ }>;
12
+
13
+ /**
14
+ * Android-specific configuration.
15
+ */
16
+ export type FloatingToolbarAndroidProps = Readonly<{
17
+ /** Material color variant: 'standard' | 'vibrant' */
18
+ variant?: string;
19
+ }>;
20
+
21
+ export interface FloatingToolbarNativeProps extends ViewProps {
22
+ /** Container color. Default: the platform's toolbar material. */
23
+ color?: ColorValue;
24
+
25
+ ios?: FloatingToolbarIOSProps;
26
+ android?: FloatingToolbarAndroidProps;
27
+ }
28
+
29
+ export default codegenNativeComponent<FloatingToolbarNativeProps>(
30
+ 'PCFloatingToolbar'
31
+ ) as HostComponent<FloatingToolbarNativeProps>;
@@ -1,14 +1,10 @@
1
1
  // SegmentedControl.tsx
2
2
  import React, { useCallback, useMemo } from 'react';
3
3
  import {
4
- Image,
5
4
  Platform,
6
5
  StyleSheet,
7
6
  type ColorValue,
8
- type ImageRequireSource,
9
- type ImageURISource,
10
7
  type StyleProp,
11
- type TextStyle,
12
8
  type ViewProps,
13
9
  type ViewStyle,
14
10
  } from 'react-native';
@@ -17,44 +13,27 @@ import NativeSegmentedControl, {
17
13
  type SegmentedControlSegment as NativeSegment,
18
14
  type SegmentedControlSelectEvent,
19
15
  } from './SegmentedControlNativeComponent';
16
+ import {
17
+ resolveIcon,
18
+ type PlatformIcon,
19
+ type PlatformIconSource,
20
+ } from './icons';
21
+ import { normalizeLabelStyle, type LabelStyle } from './labelStyle';
22
+ import type { AndroidMaterialStyle } from './sharedTypes';
20
23
 
21
24
  // Android: Minimum height to ensure visibility.
22
25
  // Fabric's shadow node measurement isn't being called on initial render,
23
26
  // so we apply a minHeight that matches Material design touch target guidelines.
24
27
  const ANDROID_MIN_HEIGHT = 48;
25
28
 
26
- /**
27
- * A single icon source.
28
- *
29
- * - `string`: shorthand for an SF Symbol name on iOS and a drawable resource
30
- * name on Android (the original API).
31
- * - `{ type: 'sfSymbol' }`: an SF Symbol. iOS only; ignored on Android.
32
- * - `{ type: 'drawable' }`: a drawable from the app's `res/drawable`.
33
- * Android only; ignored on iOS.
34
- * - `{ type: 'image' }`: an image asset (`require('./icon.png')`) or a
35
- * `{ uri }` source. Works on both platforms. Images are drawn as tinted
36
- * templates unless `tinted` is `false`.
37
- */
38
- export type SegmentedControlIconSource =
39
- | string
40
- | { type: 'sfSymbol'; name: string }
41
- | { type: 'drawable'; name: string }
42
- | {
43
- type: 'image';
44
- source: ImageRequireSource | ImageURISource;
45
- tinted?: boolean;
46
- };
29
+ /** A single icon source. Alias of {@link PlatformIconSource}. */
30
+ export type SegmentedControlIconSource = PlatformIconSource;
47
31
 
48
32
  /**
49
33
  * An icon for a segment: a single source used on both platforms, or a
50
- * per-platform pair so callers don't need to branch on `Platform.OS`.
34
+ * per-platform pair. Alias of {@link PlatformIcon}.
51
35
  */
52
- export type SegmentedControlIcon =
53
- | SegmentedControlIconSource
54
- | {
55
- ios?: SegmentedControlIconSource;
56
- android?: SegmentedControlIconSource;
57
- };
36
+ export type SegmentedControlIcon = PlatformIcon;
58
37
 
59
38
  /**
60
39
  * How labels and icons combine for segments that have an icon.
@@ -67,15 +46,8 @@ export type SegmentedControlIcon =
67
46
  */
68
47
  export type SegmentedControlLabelVisibility = 'auto' | 'labeled' | 'unlabeled';
69
48
 
70
- /**
71
- * Font for segment labels. Each field falls back to the platform default.
72
- */
73
- export interface SegmentedControlLabelStyle {
74
- fontFamily?: string;
75
- fontSize?: number;
76
- fontWeight?: TextStyle['fontWeight'];
77
- fontStyle?: 'normal' | 'italic';
78
- }
49
+ /** Font for segment labels. Alias of {@link LabelStyle}. */
50
+ export type SegmentedControlLabelStyle = LabelStyle;
79
51
 
80
52
  /**
81
53
  * Badge colors. Both default to the platform look (red with white text).
@@ -201,77 +173,20 @@ export interface SegmentedControlProps extends ViewProps {
201
173
 
202
174
  /** Outline color of the segments. */
203
175
  strokeColor?: ColorValue;
176
+
177
+ /**
178
+ * Material style: the Material 3 Expressive connected buttons (default),
179
+ * or the classic Material 3 segmented buttons.
180
+ */
181
+ material?: AndroidMaterialStyle;
204
182
  };
205
183
 
206
184
  /** Test identifier */
207
185
  testID?: string;
208
186
  }
209
187
 
210
- type NativeIconFields = Pick<
211
- NativeSegment,
212
- 'iconType' | 'iconName' | 'iconUri' | 'iconScale' | 'iconTinted'
213
- >;
214
-
215
- const NO_ICON: NativeIconFields = {
216
- iconType: '',
217
- iconName: '',
218
- iconUri: '',
219
- iconScale: 1,
220
- iconTinted: 'true',
221
- };
222
-
223
- function pickIconSource(
224
- icon: SegmentedControlIcon | undefined
225
- ): SegmentedControlIconSource | undefined {
226
- if (icon === undefined || typeof icon === 'string' || 'type' in icon) {
227
- return icon;
228
- }
229
- return Platform.OS === 'ios' ? icon.ios : icon.android;
230
- }
231
-
232
- /**
233
- * Flattens the public icon shape into the strings native expects, dropping
234
- * sources that don't apply to the current platform.
235
- */
236
- export function resolveSegmentIcon(
237
- icon: SegmentedControlIcon | undefined
238
- ): NativeIconFields {
239
- const source = pickIconSource(icon);
240
- if (source === undefined) return NO_ICON;
241
-
242
- if (typeof source === 'string') {
243
- if (source.length === 0) return NO_ICON;
244
- return {
245
- ...NO_ICON,
246
- iconType: Platform.OS === 'ios' ? 'sfSymbol' : 'drawable',
247
- iconName: source,
248
- };
249
- }
250
-
251
- switch (source.type) {
252
- case 'sfSymbol':
253
- return Platform.OS === 'ios'
254
- ? { ...NO_ICON, iconType: 'sfSymbol', iconName: source.name }
255
- : NO_ICON;
256
- case 'drawable':
257
- return Platform.OS === 'android'
258
- ? { ...NO_ICON, iconType: 'drawable', iconName: source.name }
259
- : NO_ICON;
260
- case 'image': {
261
- const resolved = Image.resolveAssetSource(source.source);
262
- if (!resolved?.uri) return NO_ICON;
263
- return {
264
- iconType: 'image',
265
- iconName: '',
266
- iconUri: resolved.uri,
267
- iconScale: resolved.scale > 0 ? resolved.scale : 1,
268
- iconTinted: source.tinted === false ? 'false' : 'true',
269
- };
270
- }
271
- default:
272
- return NO_ICON;
273
- }
274
- }
188
+ /** Flattens a segment icon for native. Alias of {@link resolveIcon}. */
189
+ export const resolveSegmentIcon = resolveIcon;
275
190
 
276
191
  function normalizeSelectedValue(selected: string | null): string {
277
192
  return selected ?? '';
@@ -346,24 +261,17 @@ export function SegmentedControl(
346
261
  (Platform.OS === 'ios' ? ios?.selectedSegmentTintColor : undefined);
347
262
 
348
263
  // Normalize the label font; empty / 0 means platform default
349
- const nativeLabelStyle = useMemo(() => {
350
- if (!labelStyle) return undefined;
351
- return {
352
- fontFamily: labelStyle.fontFamily ?? '',
353
- fontSize: labelStyle.fontSize ?? 0,
354
- fontWeight:
355
- labelStyle.fontWeight === undefined
356
- ? ''
357
- : String(labelStyle.fontWeight),
358
- fontStyle: labelStyle.fontStyle ?? '',
359
- };
360
- }, [labelStyle]);
264
+ const nativeLabelStyle = useMemo(
265
+ () => normalizeLabelStyle(labelStyle),
266
+ [labelStyle]
267
+ );
361
268
 
362
269
  // Normalize Android props
363
270
  const nativeAndroid = useMemo(() => {
364
271
  if (!android) return undefined;
365
272
  return {
366
273
  selectionRequired: android.selectionRequired === false ? 'false' : 'true',
274
+ material: android.material ?? 'expressive',
367
275
  };
368
276
  }, [android]);
369
277
 
@@ -72,6 +72,9 @@ export type IOSProps = Readonly<{
72
72
  export type AndroidProps = Readonly<{
73
73
  /** Whether one segment must always be selected */
74
74
  selectionRequired?: string; // 'true' | 'false'
75
+
76
+ /** Material style: 'm3' | 'expressive' */
77
+ material?: string;
75
78
  }>;
76
79
 
77
80
  export interface SegmentedControlProps extends ViewProps {