react-native-platform-components 0.8.10 → 1.0.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/README.md +68 -891
- package/android/src/main/java/com/platformcomponents/PCDatePickerView.kt +17 -3
- package/android/src/main/java/com/platformcomponents/PCImageLoader.kt +88 -0
- package/android/src/main/java/com/platformcomponents/PCSegmentedControlView.kt +292 -31
- package/android/src/main/java/com/platformcomponents/PCSegmentedControlViewManager.kt +74 -8
- package/android/src/main/java/com/platformcomponents/PCSelectionMenuView.kt +2 -1
- package/android/src/main/java/com/platformcomponents/PCThemeSupport.kt +94 -0
- package/android/src/main/res/values/styles.xml +40 -0
- package/ios/PCImageLoader.swift +58 -0
- package/ios/PCSegmentedControl.mm +85 -25
- package/ios/PCSegmentedControl.swift +231 -308
- package/lib/commonjs/DatePickerNativeComponent.ts +4 -2
- package/lib/commonjs/SegmentedControl.js +138 -6
- package/lib/commonjs/SegmentedControl.js.map +1 -1
- package/lib/commonjs/SegmentedControlNativeComponent.ts +62 -8
- package/lib/commonjs/SelectionMenuNativeComponent.ts +4 -2
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/module/DatePickerNativeComponent.ts +4 -2
- package/lib/module/SegmentedControl.js +138 -7
- package/lib/module/SegmentedControl.js.map +1 -1
- package/lib/module/SegmentedControlNativeComponent.ts +62 -8
- package/lib/module/SelectionMenuNativeComponent.ts +4 -2
- package/lib/module/index.web.js.map +1 -1
- package/lib/typescript/commonjs/docs/docusaurus.config.d.ts +4 -0
- package/lib/typescript/commonjs/docs/docusaurus.config.d.ts.map +1 -0
- package/lib/typescript/commonjs/docs/sidebars.d.ts +4 -0
- package/lib/typescript/commonjs/docs/sidebars.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/DatePickerNativeComponent.d.ts +2 -2
- package/lib/typescript/commonjs/src/DatePickerNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/SegmentedControl.d.ts +111 -6
- package/lib/typescript/commonjs/src/SegmentedControl.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts +46 -7
- package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/SelectionMenuNativeComponent.d.ts +2 -2
- package/lib/typescript/commonjs/src/SelectionMenuNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.web.d.ts +36 -1
- package/lib/typescript/commonjs/src/index.web.d.ts.map +1 -1
- package/lib/typescript/module/docs/docusaurus.config.d.ts +4 -0
- package/lib/typescript/module/docs/docusaurus.config.d.ts.map +1 -0
- package/lib/typescript/module/docs/sidebars.d.ts +4 -0
- package/lib/typescript/module/docs/sidebars.d.ts.map +1 -0
- package/lib/typescript/module/src/DatePicker.d.ts +1 -1
- package/lib/typescript/module/src/DatePicker.d.ts.map +1 -1
- package/lib/typescript/module/src/DatePickerNativeComponent.d.ts +2 -2
- package/lib/typescript/module/src/DatePickerNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/SegmentedControl.d.ts +111 -6
- package/lib/typescript/module/src/SegmentedControl.d.ts.map +1 -1
- package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts +46 -7
- package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/SelectionMenu.d.ts +1 -1
- package/lib/typescript/module/src/SelectionMenu.d.ts.map +1 -1
- package/lib/typescript/module/src/SelectionMenuNativeComponent.d.ts +2 -2
- package/lib/typescript/module/src/SelectionMenuNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +6 -6
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/index.web.d.ts +36 -1
- package/lib/typescript/module/src/index.web.d.ts.map +1 -1
- package/package.json +62 -37
- package/plugin/build/index.cjs +58 -17
- package/plugin/build/index.d.ts +32 -15
- package/plugin/build/index.d.ts.map +1 -1
- package/plugin/tsconfig.json +9 -4
- package/src/DatePickerNativeComponent.ts +4 -2
- package/src/SegmentedControl.tsx +238 -11
- package/src/SegmentedControlNativeComponent.ts +62 -8
- package/src/SelectionMenuNativeComponent.ts +4 -2
- package/src/index.web.tsx +34 -1
package/README.md
CHANGED
|
@@ -2,12 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/react-native-platform-components)
|
|
4
4
|
[](https://www.npmjs.com/package/react-native-platform-components)
|
|
5
|
+
[](https://github.com/JarX-Concepts/react-native-platform-components/actions/workflows/ci.yml)
|
|
6
|
+
[](./LICENSE)
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
Native **DatePicker**, **ContextMenu**, **SelectionMenu**, **SegmentedControl** and **LiquidGlass** for React Native. Every component is the real platform widget on both iOS and Android (UIKit and SwiftUI on iOS, Material 3 on Android), behind one typed, declarative API. No JavaScript re-implementations.
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
```sh
|
|
11
|
+
npm install react-native-platform-components
|
|
12
|
+
```
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
```tsx
|
|
15
|
+
import { SegmentedControl } from 'react-native-platform-components';
|
|
16
|
+
|
|
17
|
+
<SegmentedControl
|
|
18
|
+
segments={[
|
|
19
|
+
{ label: 'Day', value: 'day' },
|
|
20
|
+
{ label: 'Week', value: 'week' },
|
|
21
|
+
]}
|
|
22
|
+
selectedValue={range}
|
|
23
|
+
onSelect={setRange}
|
|
24
|
+
/>;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
That renders `UISegmentedControl` on iOS and Material 3 segmented buttons (`MaterialButtonToggleGroup`) on Android. The library needs the New Architecture: React Native 0.81+, or Expo SDK 54+ with a dev client (not Expo Go). See [Installation](#installation) and the [documentation site](https://jarx-concepts.github.io/react-native-platform-components).
|
|
28
|
+
|
|
29
|
+
## Why this library
|
|
30
|
+
|
|
31
|
+
| You need | This library | Common alternative |
|
|
32
|
+
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
33
|
+
| Date and time pickers | `UIDatePicker` on iOS; `MaterialDatePicker` / `MaterialTimePicker` or the AppCompat dialogs on Android. Declarative `visible` prop, modal or embedded. | `@react-native-community/datetimepicker`: platform dialogs on Android; Material 3 pickers are an [open request](https://github.com/react-native-datetimepicker/datetimepicker/issues/790). |
|
|
34
|
+
| Segmented control | `UISegmentedControl` on iOS; Material 3 segmented buttons on Android. Icons and badges on both. | `@react-native-segmented-control/segmented-control`: native on iOS, drawn in JavaScript on Android. |
|
|
35
|
+
| Context menu | `UIContextMenuInteraction` on iOS; `PopupMenu` on Android. Long-press gesture or modal trigger, icons on both. | `zeego`: native on both platforms, through two additional native dependencies (`react-native-ios-context-menu`, `@react-native-menu/menu`). |
|
|
36
|
+
| Selection menu | System menus on iOS; Material exposed dropdown or `Spinner` on Android. Headless or inline. | `@react-native-picker/picker`: wheel picker on iOS, dialog or dropdown `Spinner` on Android; no menu-style presentation. |
|
|
37
|
+
| Liquid glass | `UIGlassEffect` on iOS 26+, a fallback `View` elsewhere, `isLiquidGlassSupported` flag. | `@callstack/liquid-glass`, `expo-glass-effect`: same idea on iOS; here it ships with the components above. |
|
|
38
|
+
| All of the above | One package, one API shape (platform-only props under `ios={{ }}` and `android={{ }}`), Fabric + Codegen typed bindings, Expo config plugin, TypeScript. | Four or five packages with different conventions, install steps and upgrade cadences. |
|
|
39
|
+
|
|
40
|
+
## Components
|
|
41
|
+
|
|
42
|
+
| Component | iOS | Android | Docs |
|
|
43
|
+
| -------------------- | -------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
44
|
+
| **DatePicker** | `UIDatePicker` | `MaterialDatePicker` / `MaterialTimePicker` or AppCompat dialogs | [DatePicker](https://jarx-concepts.github.io/react-native-platform-components/components/datepicker) |
|
|
45
|
+
| **ContextMenu** | `UIContextMenuInteraction` | `PopupMenu` | [ContextMenu](https://jarx-concepts.github.io/react-native-platform-components/components/contextmenu) |
|
|
46
|
+
| **SelectionMenu** | System menus | Material exposed dropdown or `Spinner` | [SelectionMenu](https://jarx-concepts.github.io/react-native-platform-components/components/selectionmenu) |
|
|
47
|
+
| **SegmentedControl** | `UISegmentedControl` | `MaterialButtonToggleGroup` (Material 3 segmented buttons) | [SegmentedControl](https://jarx-concepts.github.io/react-native-platform-components/components/segmentedcontrol) |
|
|
48
|
+
| **LiquidGlass** | `UIGlassEffect` (iOS 26+) | Fallback `View` | [LiquidGlass](https://jarx-concepts.github.io/react-native-platform-components/components/liquidglass) |
|
|
11
49
|
|
|
12
50
|
<table>
|
|
13
51
|
<tr>
|
|
@@ -52,20 +90,9 @@ This library focuses on **true native behavior**, not JavaScript re-implementati
|
|
|
52
90
|
</tr>
|
|
53
91
|
</table>
|
|
54
92
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
- **DatePicker** – native date & time pickers with modal and embedded presentations
|
|
58
|
-
- **ContextMenu** – native context menus with long-press activation (UIContextMenuInteraction on iOS, PopupMenu on Android)
|
|
59
|
-
- **SelectionMenu** – native selection menus (Material on Android, system menus on iOS)
|
|
60
|
-
- **SegmentedControl** – native segmented controls (UISegmentedControl on iOS, MaterialButtonToggleGroup on Android)
|
|
61
|
-
- **LiquidGlass** – iOS 26+ glass morphism effects (UIGlassEffect on iOS, fallback View on Android)
|
|
62
|
-
|
|
63
|
-
### Goals
|
|
93
|
+
**Have a component request?** If there's a native UI component you'd like to see added, [open an issue](https://github.com/JarX-Concepts/react-native-platform-components/issues/new) describing the component and its native APIs on iOS and Android.
|
|
64
94
|
|
|
65
|
-
|
|
66
|
-
- Support modern platform design systems (Material 3 on Android, system pickers on iOS)
|
|
67
|
-
- Offer **headless** and **inline** modes for maximum layout control
|
|
68
|
-
- Integrate cleanly with **React Native Codegen / Fabric**
|
|
95
|
+
**Using it in a shipped app?** Open a PR to list it here.
|
|
69
96
|
|
|
70
97
|
---
|
|
71
98
|
|
|
@@ -77,802 +104,11 @@ npm install react-native-platform-components
|
|
|
77
104
|
yarn add react-native-platform-components
|
|
78
105
|
```
|
|
79
106
|
|
|
80
|
-
|
|
107
|
+
**iOS:** `cd ios && pod install`. Nothing else to configure.
|
|
81
108
|
|
|
82
|
-
|
|
83
|
-
cd ios
|
|
84
|
-
pod install
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
- Minimum iOS version: **iOS 13+**
|
|
88
|
-
- Uses `UIDatePicker`, SwiftUI Menu, and `UIContextMenuInteraction`
|
|
89
|
-
|
|
90
|
-
### Android
|
|
91
|
-
|
|
92
|
-
- Uses native Android Views with Material Design (including `PopupMenu` for context menus)
|
|
93
|
-
- Supports **Material 3** styling
|
|
94
|
-
- **⚠️ Your app may crash if theme is not configured** — See [Android Theme Configuration](#android-theme-configuration) below
|
|
95
|
-
|
|
96
|
-
### Expo (Managed Workflow)
|
|
97
|
-
|
|
98
|
-
> **Note:** This library is **not supported in Expo Go**. It requires native code and must be used with [Expo Dev Client](https://docs.expo.dev/develop/development-builds/introduction/) or EAS Build.
|
|
99
|
-
|
|
100
|
-
```sh
|
|
101
|
-
npx expo install react-native-platform-components
|
|
102
|
-
npx expo prebuild
|
|
103
|
-
npx expo run:ios
|
|
104
|
-
# or
|
|
105
|
-
npx expo run:android
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
The library includes an Expo config plugin that handles all native configuration automatically. No manual native setup is required.
|
|
109
|
+
**Android:** nothing to configure. The components work with the default AppCompat app theme; give the app a Material 3 theme to have `SegmentedControl` and the Material pickers use your colors. See [Android Theme Configuration](https://jarx-concepts.github.io/react-native-platform-components/guides/android-theme).
|
|
109
110
|
|
|
110
|
-
**EAS Build
|
|
111
|
-
|
|
112
|
-
```sh
|
|
113
|
-
eas build --platform ios
|
|
114
|
-
eas build --platform android
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
**Config Plugin:**
|
|
118
|
-
|
|
119
|
-
Add to your `app.json`:
|
|
120
|
-
|
|
121
|
-
```json
|
|
122
|
-
{
|
|
123
|
-
"expo": {
|
|
124
|
-
"plugins": [["react-native-platform-components/app.plugin", {}]]
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
For a complete working example, see the [`example-expo/`](./example-expo) directory.
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
## React Native New Architecture
|
|
134
|
-
|
|
135
|
-
This library is built for the **React Native New Architecture** (Fabric + TurboModules).
|
|
136
|
-
|
|
137
|
-
| Feature | Status |
|
|
138
|
-
| --------------------- | ---------------------------------- |
|
|
139
|
-
| Fabric (New Renderer) | Supported |
|
|
140
|
-
| Codegen | Used for type-safe native bindings |
|
|
141
|
-
| TurboModules | N/A (view components only) |
|
|
142
|
-
| Old Architecture | Not supported |
|
|
143
|
-
|
|
144
|
-
**Tested with:**
|
|
145
|
-
|
|
146
|
-
- React Native 0.81+ (bare and Expo)
|
|
147
|
-
- Expo SDK 54+
|
|
148
|
-
|
|
149
|
-
**Requirements:**
|
|
150
|
-
|
|
151
|
-
- New Architecture must be enabled in your app
|
|
152
|
-
- For bare React Native: set `newArchEnabled=true` in `gradle.properties` (Android) and use the `RCT_NEW_ARCH_ENABLED` flag (iOS)
|
|
153
|
-
- For Expo: set `"newArchEnabled": true` in `app.json`
|
|
154
|
-
|
|
155
|
-
---
|
|
156
|
-
|
|
157
|
-
## Quick Start
|
|
158
|
-
|
|
159
|
-
### DatePicker (Modal)
|
|
160
|
-
|
|
161
|
-
```tsx
|
|
162
|
-
import { DatePicker } from 'react-native-platform-components';
|
|
163
|
-
|
|
164
|
-
export function Example() {
|
|
165
|
-
const [date, setDate] = React.useState<Date | null>(null);
|
|
166
|
-
const [visible, setVisible] = React.useState(false);
|
|
167
|
-
|
|
168
|
-
return (
|
|
169
|
-
<>
|
|
170
|
-
<Button title="Pick date" onPress={() => setVisible(true)} />
|
|
171
|
-
|
|
172
|
-
<DatePicker
|
|
173
|
-
date={date}
|
|
174
|
-
visible={visible}
|
|
175
|
-
presentation="modal"
|
|
176
|
-
mode="date"
|
|
177
|
-
onConfirm={(d, confirmed) => {
|
|
178
|
-
setDate(d);
|
|
179
|
-
if (confirmed) setVisible(false);
|
|
180
|
-
}}
|
|
181
|
-
onClosed={() => setVisible(false)}
|
|
182
|
-
ios={{ preferredStyle: 'inline' }}
|
|
183
|
-
android={{ material: 'system' }}
|
|
184
|
-
/>
|
|
185
|
-
</>
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### DatePicker (Embedded)
|
|
191
|
-
|
|
192
|
-
```tsx
|
|
193
|
-
import { DatePicker } from 'react-native-platform-components';
|
|
194
|
-
|
|
195
|
-
export function Example() {
|
|
196
|
-
const [date, setDate] = React.useState<Date | null>(new Date());
|
|
197
|
-
|
|
198
|
-
return (
|
|
199
|
-
<DatePicker
|
|
200
|
-
date={date}
|
|
201
|
-
presentation="embedded"
|
|
202
|
-
mode="date"
|
|
203
|
-
onConfirm={(d, confirmed) => setDate(d)}
|
|
204
|
-
ios={{ preferredStyle: 'inline' }}
|
|
205
|
-
android={{ material: 'system' }}
|
|
206
|
-
/>
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
---
|
|
212
|
-
|
|
213
|
-
### ContextMenu (Gesture Mode)
|
|
214
|
-
|
|
215
|
-
```tsx
|
|
216
|
-
import { ContextMenu } from 'react-native-platform-components';
|
|
217
|
-
import { Platform, View, Text } from 'react-native';
|
|
218
|
-
|
|
219
|
-
export function Example() {
|
|
220
|
-
const [lastAction, setLastAction] = React.useState<string | null>(null);
|
|
221
|
-
|
|
222
|
-
return (
|
|
223
|
-
<ContextMenu
|
|
224
|
-
title="Options"
|
|
225
|
-
actions={[
|
|
226
|
-
{
|
|
227
|
-
id: 'copy',
|
|
228
|
-
title: 'Copy',
|
|
229
|
-
image: Platform.OS === 'ios' ? 'doc.on.doc' : 'content_copy',
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
id: 'share',
|
|
233
|
-
title: 'Share',
|
|
234
|
-
image: Platform.OS === 'ios' ? 'square.and.arrow.up' : 'share',
|
|
235
|
-
},
|
|
236
|
-
{
|
|
237
|
-
id: 'delete',
|
|
238
|
-
title: 'Delete',
|
|
239
|
-
image: Platform.OS === 'ios' ? 'trash' : 'delete',
|
|
240
|
-
attributes: { destructive: true },
|
|
241
|
-
},
|
|
242
|
-
]}
|
|
243
|
-
onPressAction={(id, title) => setLastAction(title)}
|
|
244
|
-
>
|
|
245
|
-
<View
|
|
246
|
-
style={{ padding: 20, backgroundColor: '#E8F4FD', borderRadius: 8 }}
|
|
247
|
-
>
|
|
248
|
-
<Text>Long-press me</Text>
|
|
249
|
-
</View>
|
|
250
|
-
</ContextMenu>
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### ContextMenu (Modal Mode)
|
|
256
|
-
|
|
257
|
-
```tsx
|
|
258
|
-
import { ContextMenu } from 'react-native-platform-components';
|
|
259
|
-
import { View, Text } from 'react-native';
|
|
260
|
-
|
|
261
|
-
export function Example() {
|
|
262
|
-
return (
|
|
263
|
-
<ContextMenu
|
|
264
|
-
title="Actions"
|
|
265
|
-
actions={[
|
|
266
|
-
{ id: 'edit', title: 'Edit' },
|
|
267
|
-
{ id: 'duplicate', title: 'Duplicate' },
|
|
268
|
-
{ id: 'delete', title: 'Delete', attributes: { destructive: true } },
|
|
269
|
-
]}
|
|
270
|
-
trigger="tap" // or "longPress" (default)
|
|
271
|
-
onPressAction={(id) => console.log('Selected:', id)}
|
|
272
|
-
>
|
|
273
|
-
<View style={{ padding: 16, backgroundColor: '#eee' }}>
|
|
274
|
-
<Text>Tap or long-press me</Text>
|
|
275
|
-
</View>
|
|
276
|
-
</ContextMenu>
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
---
|
|
282
|
-
|
|
283
|
-
### SelectionMenu (Headless)
|
|
284
|
-
|
|
285
|
-
```tsx
|
|
286
|
-
import { SelectionMenu } from 'react-native-platform-components';
|
|
287
|
-
|
|
288
|
-
const options = [
|
|
289
|
-
{ label: 'Apple', data: 'apple' },
|
|
290
|
-
{ label: 'Banana', data: 'banana' },
|
|
291
|
-
{ label: 'Orange', data: 'orange' },
|
|
292
|
-
];
|
|
293
|
-
|
|
294
|
-
export function Example() {
|
|
295
|
-
const [visible, setVisible] = React.useState(false);
|
|
296
|
-
const [value, setValue] = React.useState<string | null>(null);
|
|
297
|
-
|
|
298
|
-
return (
|
|
299
|
-
<>
|
|
300
|
-
<Button title="Open menu" onPress={() => setVisible(true)} />
|
|
301
|
-
|
|
302
|
-
<SelectionMenu
|
|
303
|
-
options={options}
|
|
304
|
-
selected={value}
|
|
305
|
-
visible={visible}
|
|
306
|
-
onSelect={(data) => {
|
|
307
|
-
setValue(data);
|
|
308
|
-
setVisible(false);
|
|
309
|
-
}}
|
|
310
|
-
onRequestClose={() => setVisible(false)}
|
|
311
|
-
/>
|
|
312
|
-
</>
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
### SelectionMenu (Inline)
|
|
318
|
-
|
|
319
|
-
```tsx
|
|
320
|
-
import { SelectionMenu } from 'react-native-platform-components';
|
|
321
|
-
|
|
322
|
-
const options = [
|
|
323
|
-
{ label: 'Apple', data: 'apple' },
|
|
324
|
-
{ label: 'Banana', data: 'banana' },
|
|
325
|
-
{ label: 'Orange', data: 'orange' },
|
|
326
|
-
];
|
|
327
|
-
|
|
328
|
-
export function Example() {
|
|
329
|
-
const [value, setValue] = React.useState<string | null>(null);
|
|
330
|
-
|
|
331
|
-
return (
|
|
332
|
-
<SelectionMenu
|
|
333
|
-
options={options}
|
|
334
|
-
selected={value}
|
|
335
|
-
presentation="embedded"
|
|
336
|
-
placeholder="Select fruit"
|
|
337
|
-
onSelect={(data) => setValue(data)}
|
|
338
|
-
android={{ material: 'm3' }}
|
|
339
|
-
/>
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
---
|
|
345
|
-
|
|
346
|
-
### SegmentedControl
|
|
347
|
-
|
|
348
|
-
```tsx
|
|
349
|
-
import { SegmentedControl } from 'react-native-platform-components';
|
|
350
|
-
|
|
351
|
-
const segments = [
|
|
352
|
-
{ label: 'Day', value: 'day' },
|
|
353
|
-
{ label: 'Week', value: 'week' },
|
|
354
|
-
{ label: 'Month', value: 'month' },
|
|
355
|
-
];
|
|
356
|
-
|
|
357
|
-
export function Example() {
|
|
358
|
-
const [selected, setSelected] = React.useState('day');
|
|
359
|
-
|
|
360
|
-
return (
|
|
361
|
-
<SegmentedControl
|
|
362
|
-
segments={segments}
|
|
363
|
-
selectedValue={selected}
|
|
364
|
-
onSelect={(value) => setSelected(value)}
|
|
365
|
-
/>
|
|
366
|
-
);
|
|
367
|
-
}
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
### SegmentedControl (With Icons)
|
|
371
|
-
|
|
372
|
-
```tsx
|
|
373
|
-
import { SegmentedControl } from 'react-native-platform-components';
|
|
374
|
-
import { Platform } from 'react-native';
|
|
375
|
-
|
|
376
|
-
const segments = [
|
|
377
|
-
{
|
|
378
|
-
label: 'List',
|
|
379
|
-
value: 'list',
|
|
380
|
-
icon: Platform.OS === 'ios' ? 'list.bullet' : 'list_bullet',
|
|
381
|
-
},
|
|
382
|
-
{
|
|
383
|
-
label: 'Grid',
|
|
384
|
-
value: 'grid',
|
|
385
|
-
icon: Platform.OS === 'ios' ? 'square.grid.2x2' : 'grid_view',
|
|
386
|
-
},
|
|
387
|
-
];
|
|
388
|
-
|
|
389
|
-
export function Example() {
|
|
390
|
-
const [selected, setSelected] = React.useState('list');
|
|
391
|
-
|
|
392
|
-
return (
|
|
393
|
-
<SegmentedControl
|
|
394
|
-
segments={segments}
|
|
395
|
-
selectedValue={selected}
|
|
396
|
-
onSelect={(value) => setSelected(value)}
|
|
397
|
-
ios={{ apportionsSegmentWidthsByContent: true }}
|
|
398
|
-
/>
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
---
|
|
404
|
-
|
|
405
|
-
### LiquidGlass
|
|
406
|
-
|
|
407
|
-
```tsx
|
|
408
|
-
import {
|
|
409
|
-
LiquidGlass,
|
|
410
|
-
isLiquidGlassSupported,
|
|
411
|
-
} from 'react-native-platform-components';
|
|
412
|
-
import { View, Text, Image } from 'react-native';
|
|
413
|
-
|
|
414
|
-
export function Example() {
|
|
415
|
-
return (
|
|
416
|
-
<View style={{ flex: 1 }}>
|
|
417
|
-
{/* Background content */}
|
|
418
|
-
<Image
|
|
419
|
-
source={{ uri: 'https://example.com/photo.jpg' }}
|
|
420
|
-
style={{ flex: 1 }}
|
|
421
|
-
/>
|
|
422
|
-
|
|
423
|
-
{/* Glass effect overlay */}
|
|
424
|
-
<LiquidGlass
|
|
425
|
-
style={{
|
|
426
|
-
position: 'absolute',
|
|
427
|
-
top: 50,
|
|
428
|
-
left: 20,
|
|
429
|
-
right: 20,
|
|
430
|
-
padding: 20,
|
|
431
|
-
}}
|
|
432
|
-
cornerRadius={20}
|
|
433
|
-
ios={{
|
|
434
|
-
effect: 'regular',
|
|
435
|
-
interactive: true,
|
|
436
|
-
colorScheme: 'system',
|
|
437
|
-
}}
|
|
438
|
-
android={{
|
|
439
|
-
fallbackBackgroundColor: '#FFFFFF80',
|
|
440
|
-
}}
|
|
441
|
-
>
|
|
442
|
-
<Text style={{ fontSize: 18, fontWeight: '600' }}>
|
|
443
|
-
{isLiquidGlassSupported ? 'Glass Effect!' : 'Fallback View'}
|
|
444
|
-
</Text>
|
|
445
|
-
</LiquidGlass>
|
|
446
|
-
</View>
|
|
447
|
-
);
|
|
448
|
-
}
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
---
|
|
452
|
-
|
|
453
|
-
## Components
|
|
454
|
-
|
|
455
|
-
## DatePicker
|
|
456
|
-
|
|
457
|
-
Native date & time picker using **platform system pickers**.
|
|
458
|
-
|
|
459
|
-
### Props
|
|
460
|
-
|
|
461
|
-
| Prop | Type | Description |
|
|
462
|
-
| -------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
463
|
-
| `date` | `Date \| null` | Controlled date value |
|
|
464
|
-
| `minDate` | `Date \| null` | Minimum selectable date |
|
|
465
|
-
| `maxDate` | `Date \| null` | Maximum selectable date |
|
|
466
|
-
| `locale` | `string` | Locale identifier (e.g., `'en-US'`) |
|
|
467
|
-
| `timeZoneName` | `string` | Time zone identifier |
|
|
468
|
-
| `mode` | `'date' \| 'time' \| 'dateAndTime' \| 'countDownTimer'` | Picker mode |
|
|
469
|
-
| `presentation` | `'modal' \| 'embedded'` | Presentation style |
|
|
470
|
-
| `visible` | `boolean` | Controls modal visibility (modal mode only) |
|
|
471
|
-
| `onConfirm` | `(date: Date, confirmed: boolean) => void` | Called on date change; `confirmed` is `true` for deliberate selections (see below) |
|
|
472
|
-
| `onClosed` | `() => void` | Called when modal is dismissed |
|
|
473
|
-
|
|
474
|
-
### iOS Props (`ios`)
|
|
475
|
-
|
|
476
|
-
| Prop | Type | Description |
|
|
477
|
-
| -------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
478
|
-
| `preferredStyle` | `'automatic' \| 'compact' \| 'inline' \| 'wheels'` | iOS date picker style |
|
|
479
|
-
| `countDownDurationSeconds` | `number` | Duration for countdown timer mode |
|
|
480
|
-
| `minuteInterval` | `number` | Minute interval (1-30) |
|
|
481
|
-
| `roundsToMinuteInterval` | `'inherit' \| 'round' \| 'noRound'` | Rounding behavior |
|
|
482
|
-
| `showConfirmToolbar` | `boolean` | Modal only. Show Cancel/Done toolbar below the picker. Defaults to `true`. See below. |
|
|
483
|
-
|
|
484
|
-
### Android Props (`android`)
|
|
485
|
-
|
|
486
|
-
| Prop | Type | Description |
|
|
487
|
-
| --------------------- | ------------------ | ---------------------------------------------------------------------- |
|
|
488
|
-
| `firstDayOfWeek` | `number` | First day of week (1-7, Sunday=1) |
|
|
489
|
-
| `material` | `'system' \| 'm3'` | Material Design style (modal only; embedded always uses system picker) |
|
|
490
|
-
| `dialogTitle` | `string` | Custom dialog title |
|
|
491
|
-
| `positiveButtonTitle` | `string` | Custom confirm button text |
|
|
492
|
-
| `negativeButtonTitle` | `string` | Custom cancel button text |
|
|
493
|
-
|
|
494
|
-
### The `confirmed` Flag
|
|
495
|
-
|
|
496
|
-
`onConfirm` fires on every date/time change, but the second argument (`confirmed`) lets you distinguish between browsing and deliberate selections:
|
|
497
|
-
|
|
498
|
-
| Platform / Mode | Every change | Deliberate selection |
|
|
499
|
-
| -------------------- | ----------------------------------------- | ------------------------------------- |
|
|
500
|
-
| **iOS modal** | `confirmed: false` (user still adjusting) | `confirmed: true` (tapping **Done**) |
|
|
501
|
-
| **iOS embedded** | `confirmed: true` | — |
|
|
502
|
-
| **Android modal** | — | `confirmed: true` (pressing OK) |
|
|
503
|
-
| **Android embedded** | `confirmed: true` | — |
|
|
504
|
-
|
|
505
|
-
On iOS in modal presentation, the picker is shown in a popover with a Cancel/Done toolbar below it. Tapping **Done** emits `confirmed: true`; tapping **Cancel** or outside the popover calls `onClosed`. Set `ios.showConfirmToolbar: false` to hide the toolbar — in that mode `confirmed: true` never fires, and your app is expected to drive dismissal by flipping `visible` off (reading the current date from the stream of `confirmed: false` events). This only makes UX sense paired with `ios.preferredStyle: 'inline'`.
|
|
506
|
-
|
|
507
|
-
A common pattern is to close the modal only on a confirmed selection:
|
|
508
|
-
|
|
509
|
-
```tsx
|
|
510
|
-
<DatePicker
|
|
511
|
-
date={date}
|
|
512
|
-
visible={visible}
|
|
513
|
-
presentation="modal"
|
|
514
|
-
mode="time"
|
|
515
|
-
onConfirm={(d, confirmed) => {
|
|
516
|
-
setDate(d);
|
|
517
|
-
if (confirmed) setVisible(false);
|
|
518
|
-
}}
|
|
519
|
-
onClosed={() => setVisible(false)}
|
|
520
|
-
/>
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
---
|
|
524
|
-
|
|
525
|
-
## ContextMenu
|
|
526
|
-
|
|
527
|
-
Native context menu that wraps content and responds to **long-press** or **tap** gestures.
|
|
528
|
-
|
|
529
|
-
### Props
|
|
530
|
-
|
|
531
|
-
| Prop | Type | Description |
|
|
532
|
-
| --------------- | --------------------------------- | ------------------------------------------- |
|
|
533
|
-
| `title` | `string` | Menu title (shown as header on iOS) |
|
|
534
|
-
| `actions` | `ContextMenuAction[]` | Array of menu actions |
|
|
535
|
-
| `disabled` | `boolean` | Disables the menu |
|
|
536
|
-
| `trigger` | `'longPress' \| 'tap'` | How the menu opens (default: `'longPress'`) |
|
|
537
|
-
| `onPressAction` | `(actionId, actionTitle) => void` | Called when user selects an action |
|
|
538
|
-
| `onMenuOpen` | `() => void` | Called when menu opens |
|
|
539
|
-
| `onMenuClose` | `() => void` | Called when menu closes |
|
|
540
|
-
| `children` | `ReactNode` | Content to wrap (required) |
|
|
541
|
-
|
|
542
|
-
### ContextMenuAction
|
|
543
|
-
|
|
544
|
-
| Property | Type | Description |
|
|
545
|
-
| ------------ | -------------------------------------- | ------------------------------------------------- |
|
|
546
|
-
| `id` | `string` | Unique identifier returned in callbacks |
|
|
547
|
-
| `title` | `string` | Display text |
|
|
548
|
-
| `subtitle` | `string` | Secondary text (iOS only) |
|
|
549
|
-
| `image` | `string` | Icon name (SF Symbol on iOS, drawable on Android) |
|
|
550
|
-
| `imageColor` | `string` | Tint color for the icon (hex string) |
|
|
551
|
-
| `attributes` | `{ destructive?, disabled?, hidden? }` | Action attributes |
|
|
552
|
-
| `state` | `'off' \| 'on' \| 'mixed'` | Checkmark state |
|
|
553
|
-
| `subactions` | `ContextMenuAction[]` | Nested actions for submenu |
|
|
554
|
-
|
|
555
|
-
### iOS Props (`ios`)
|
|
556
|
-
|
|
557
|
-
| Prop | Type | Description |
|
|
558
|
-
| --------------- | --------- | --------------------------------- |
|
|
559
|
-
| `enablePreview` | `boolean` | Enable preview when long-pressing |
|
|
560
|
-
|
|
561
|
-
### Android Props (`android`)
|
|
562
|
-
|
|
563
|
-
| Prop | Type | Description |
|
|
564
|
-
| ---------------- | ------------------- | ---------------------------------------------- |
|
|
565
|
-
| `anchorPosition` | `'left' \| 'right'` | Anchor position for the popup menu |
|
|
566
|
-
| `visible` | `boolean` | Programmatic visibility control (Android only) |
|
|
567
|
-
|
|
568
|
-
### Trigger Modes
|
|
569
|
-
|
|
570
|
-
- **Long-Press** (default): Long-press on wrapped content triggers the menu.
|
|
571
|
-
- **Tap** (`trigger="tap"`): Single tap on wrapped content triggers the menu.
|
|
572
|
-
- **Programmatic** (Android only): Use `android.visible` to control menu visibility programmatically. iOS does not support programmatic menu opening due to platform limitations.
|
|
573
|
-
|
|
574
|
-
### Icon Support
|
|
575
|
-
|
|
576
|
-
- **iOS**: Use SF Symbol names (e.g., `'trash'`, `'square.and.arrow.up'`, `'doc.on.doc'`)
|
|
577
|
-
- **Android**: Use drawable resource names or Material icon names
|
|
578
|
-
|
|
579
|
-
---
|
|
580
|
-
|
|
581
|
-
## SelectionMenu
|
|
582
|
-
|
|
583
|
-
Native selection menu with **modal** and **embedded** modes.
|
|
584
|
-
|
|
585
|
-
### Props
|
|
586
|
-
|
|
587
|
-
| Prop | Type | Description |
|
|
588
|
-
| ------------------ | ----------------------------------- | ----------------------------------------------- |
|
|
589
|
-
| `options` | `{ label: string; data: string }[]` | Array of options to display |
|
|
590
|
-
| `selected` | `string \| null` | Currently selected option's `data` value |
|
|
591
|
-
| `disabled` | `boolean` | Disables the menu |
|
|
592
|
-
| `placeholder` | `string` | Placeholder text when no selection |
|
|
593
|
-
| `presentation` | `'modal' \| 'embedded'` | Presentation mode (default: `'modal'`) |
|
|
594
|
-
| `visible` | `boolean` | Controls modal mode menu visibility |
|
|
595
|
-
| `onSelect` | `(data, label, index) => void` | Called when user selects an option |
|
|
596
|
-
| `onRequestClose` | `() => void` | Called when menu is dismissed without selection |
|
|
597
|
-
| `android.material` | `'system' \| 'm3'` | Material Design style preference |
|
|
598
|
-
|
|
599
|
-
### Modes
|
|
600
|
-
|
|
601
|
-
- **Modal mode** (default): Menu visibility controlled by `visible` prop. Use for custom trigger UI.
|
|
602
|
-
- **Embedded mode** (`presentation="embedded"`): Native picker UI rendered inline. Menu managed internally.
|
|
603
|
-
|
|
604
|
-
> **Note:** On iOS, modal mode uses a custom popover to enable programmatic presentation. For the full native menu experience (system animations, scroll physics), use embedded mode. This is an intentional trade-off: modal gives you control over the trigger UI, embedded gives you the complete system menu behavior.
|
|
605
|
-
|
|
606
|
-
---
|
|
607
|
-
|
|
608
|
-
## SegmentedControl
|
|
609
|
-
|
|
610
|
-
Native segmented control using **UISegmentedControl** on iOS and **MaterialButtonToggleGroup** on Android.
|
|
611
|
-
|
|
612
|
-
### Props
|
|
613
|
-
|
|
614
|
-
| Prop | Type | Description |
|
|
615
|
-
| --------------- | ---------------------------------------- | ------------------------------------ |
|
|
616
|
-
| `segments` | `SegmentedControlSegment[]` | Array of segments to display |
|
|
617
|
-
| `selectedValue` | `string \| null` | Currently selected segment's `value` |
|
|
618
|
-
| `disabled` | `boolean` | Disables the entire control |
|
|
619
|
-
| `onSelect` | `(value: string, index: number) => void` | Called when user selects a segment |
|
|
620
|
-
|
|
621
|
-
### SegmentedControlSegment
|
|
622
|
-
|
|
623
|
-
| Property | Type | Description |
|
|
624
|
-
| ---------- | --------- | ------------------------------------------------- |
|
|
625
|
-
| `label` | `string` | Display text for the segment |
|
|
626
|
-
| `value` | `string` | Unique value returned in callbacks |
|
|
627
|
-
| `disabled` | `boolean` | Disables this specific segment |
|
|
628
|
-
| `icon` | `string` | Icon name (SF Symbol on iOS, drawable on Android) |
|
|
629
|
-
|
|
630
|
-
### iOS Props (`ios`)
|
|
631
|
-
|
|
632
|
-
| Prop | Type | Description |
|
|
633
|
-
| ---------------------------------- | --------- | --------------------------------------------------- |
|
|
634
|
-
| `momentary` | `boolean` | If true, segments don't show selected state |
|
|
635
|
-
| `apportionsSegmentWidthsByContent` | `boolean` | If true, segment widths are proportional to content |
|
|
636
|
-
| `selectedSegmentTintColor` | `string` | Tint color for selected segment (hex string) |
|
|
637
|
-
|
|
638
|
-
### Android Props (`android`)
|
|
639
|
-
|
|
640
|
-
| Prop | Type | Description |
|
|
641
|
-
| ------------------- | --------- | -------------------------------------------- |
|
|
642
|
-
| `selectionRequired` | `boolean` | If true, one segment must always be selected |
|
|
643
|
-
|
|
644
|
-
### Icon Support
|
|
645
|
-
|
|
646
|
-
Icons work the same as ContextMenu:
|
|
647
|
-
|
|
648
|
-
- **iOS**: Use SF Symbol names (e.g., `'list.bullet'`, `'square.grid.2x2'`)
|
|
649
|
-
- **Android**: Use drawable resource names (e.g., `'list_bullet'`, `'grid_view'`)
|
|
650
|
-
|
|
651
|
-
---
|
|
652
|
-
|
|
653
|
-
## LiquidGlass
|
|
654
|
-
|
|
655
|
-
Native glass morphism effect using **UIGlassEffect** on iOS 26+. On Android and older iOS versions, renders as a regular View with optional fallback styling.
|
|
656
|
-
|
|
657
|
-
> **Note:** LiquidGlass requires **iOS 26+** (Xcode 16+). On older iOS versions and Android, the component renders children without the glass effect. Use `isLiquidGlassSupported` to check availability and provide fallback UI.
|
|
658
|
-
|
|
659
|
-
### Props
|
|
660
|
-
|
|
661
|
-
| Prop | Type | Description |
|
|
662
|
-
| -------------- | ----------- | ------------------------------------------------- |
|
|
663
|
-
| `cornerRadius` | `number` | Corner radius for the glass effect (default: `0`) |
|
|
664
|
-
| `children` | `ReactNode` | Content to render inside the glass container |
|
|
665
|
-
|
|
666
|
-
### iOS Props (`ios`)
|
|
667
|
-
|
|
668
|
-
| Prop | Type | Description |
|
|
669
|
-
| ------------- | -------------------------------- | ---------------------------------------------------- |
|
|
670
|
-
| `effect` | `'clear' \| 'regular' \| 'none'` | Glass effect intensity (default: `'regular'`) |
|
|
671
|
-
| `interactive` | `boolean` | Enable touch interaction feedback (default: `false`) |
|
|
672
|
-
| `tintColor` | `string` | Overlay tint color (hex string) |
|
|
673
|
-
| `colorScheme` | `'light' \| 'dark' \| 'system'` | Appearance mode (default: `'system'`) |
|
|
674
|
-
|
|
675
|
-
### Android Props (`android`)
|
|
676
|
-
|
|
677
|
-
| Prop | Type | Description |
|
|
678
|
-
| ------------------------- | -------- | ---------------------------------------------- |
|
|
679
|
-
| `fallbackBackgroundColor` | `string` | Background color when glass effect unavailable |
|
|
680
|
-
|
|
681
|
-
### Constants
|
|
682
|
-
|
|
683
|
-
| Export | Type | Description |
|
|
684
|
-
| ------------------------ | --------- | ------------------------------------ |
|
|
685
|
-
| `isLiquidGlassSupported` | `boolean` | `true` on iOS 26+, `false` otherwise |
|
|
686
|
-
|
|
687
|
-
### Effect Modes
|
|
688
|
-
|
|
689
|
-
- **`'regular'`** (default): Standard glass blur intensity with full glass morphism effect
|
|
690
|
-
- **`'clear'`**: More transparent, subtle glass effect
|
|
691
|
-
- **`'none'`**: No glass effect (useful for animating materialization/dematerialization)
|
|
692
|
-
|
|
693
|
-
### Platform Behavior
|
|
694
|
-
|
|
695
|
-
| Platform | iOS 26+ | iOS < 26 | Android |
|
|
696
|
-
| ------------- | ------------------- | ----------- | ------------ |
|
|
697
|
-
| Glass Effect | Full glass morphism | No effect | No effect |
|
|
698
|
-
| Corner Radius | Applied | Applied | Applied |
|
|
699
|
-
| Tint Color | Supported | Ignored | Ignored |
|
|
700
|
-
| Interactive | Supported | Ignored | Ignored |
|
|
701
|
-
| Fallback BG | N/A | Transparent | Configurable |
|
|
702
|
-
|
|
703
|
-
### Usage Tips
|
|
704
|
-
|
|
705
|
-
1. **Check support first**: Use `isLiquidGlassSupported` to conditionally render fallback UI
|
|
706
|
-
2. **Background content**: Glass effects work best over images or colorful backgrounds
|
|
707
|
-
3. **Interactive mode**: Only applies on mount; cannot be toggled after initial render
|
|
708
|
-
4. **Android fallback**: Set `android.fallbackBackgroundColor` for a semi-transparent background
|
|
709
|
-
|
|
710
|
-
---
|
|
711
|
-
|
|
712
|
-
## Design Philosophy
|
|
713
|
-
|
|
714
|
-
- **Native first** — no JS re-implementation of pickers
|
|
715
|
-
- **Headless-friendly** — works with any custom UI
|
|
716
|
-
- **Codegen-safe** — string unions & sentinel values for type safety
|
|
717
|
-
- **Predictable behavior** — no surprise re-renders or layout hacks
|
|
718
|
-
- **Platform conventions** — respects native UX patterns
|
|
719
|
-
|
|
720
|
-
---
|
|
721
|
-
|
|
722
|
-
## Theming
|
|
723
|
-
|
|
724
|
-
This library does not expose theming props. Components inherit their appearance from your app's native platform theme.
|
|
725
|
-
|
|
726
|
-
- **iOS**: Components follow system appearance (light/dark mode) and use system-defined styles (e.g., `UIBlurEffect` for menu backgrounds). These are not customizable per-component.
|
|
727
|
-
- **Android**: Components respect your app's Material Theme. Customize via your `styles.xml` or Material 3 theme configuration.
|
|
728
|
-
|
|
729
|
-
This is intentional. The goal is native fidelity, not pixel-level customization. If you need custom styling beyond what the platform theme provides, this library may not be the right fit.
|
|
730
|
-
|
|
731
|
-
---
|
|
732
|
-
|
|
733
|
-
## Color Formats
|
|
734
|
-
|
|
735
|
-
All color props in this library support the same formats as React Native's `backgroundColor`:
|
|
736
|
-
|
|
737
|
-
| Format | Example | Description |
|
|
738
|
-
| ------ | --------------------------------- | ---------------------------------- |
|
|
739
|
-
| Hex | `#RGB`, `#RRGGBB`, `#RRGGBBAA` | Standard hex colors |
|
|
740
|
-
| RGB | `rgb(255, 0, 0)` | RGB values (0-255) |
|
|
741
|
-
| RGBA | `rgba(255, 0, 0, 0.5)` | RGB with alpha (0-1) |
|
|
742
|
-
| HSL | `hsl(0, 100%, 50%)` | Hue (0-360), saturation, lightness |
|
|
743
|
-
| HSLA | `hsla(0, 100%, 50%, 0.5)` | HSL with alpha (0-1) |
|
|
744
|
-
| Named | `red`, `steelblue`, `transparent` | CSS named colors |
|
|
745
|
-
|
|
746
|
-
**Props that accept colors:**
|
|
747
|
-
|
|
748
|
-
- `ContextMenu`: `imageColor` (icon tint)
|
|
749
|
-
- `SegmentedControl`: `ios.selectedSegmentTintColor`
|
|
750
|
-
- `LiquidGlass`: `ios.tintColor`, `android.fallbackBackgroundColor`
|
|
751
|
-
|
|
752
|
-
```tsx
|
|
753
|
-
// All of these are equivalent
|
|
754
|
-
<ContextMenu actions={[{ id: '1', title: 'Red', imageColor: '#FF0000' }]} />
|
|
755
|
-
<ContextMenu actions={[{ id: '1', title: 'Red', imageColor: 'rgb(255, 0, 0)' }]} />
|
|
756
|
-
<ContextMenu actions={[{ id: '1', title: 'Red', imageColor: 'hsl(0, 100%, 50%)' }]} />
|
|
757
|
-
<ContextMenu actions={[{ id: '1', title: 'Red', imageColor: 'red' }]} />
|
|
758
|
-
```
|
|
759
|
-
|
|
760
|
-
---
|
|
761
|
-
|
|
762
|
-
## Android Theme Configuration
|
|
763
|
-
|
|
764
|
-
> **⚠️ Your app may hard crash if you skip this section.** Android components require specific theme configuration. Components can crash immediately on mount if the required theme attributes are missing.
|
|
765
|
-
|
|
766
|
-
### Theme Requirements by Component
|
|
767
|
-
|
|
768
|
-
| Component | Mode | Required Theme | Crash if Missing |
|
|
769
|
-
| -------------------- | ---------------------------- | ------------------- | ---------------- |
|
|
770
|
-
| **SegmentedControl** | (always M3) | `Theme.Material3.*` | ✅ Yes |
|
|
771
|
-
| **DatePicker** | `android.material: 'm3'` | `Theme.Material3.*` | ✅ Yes |
|
|
772
|
-
| **DatePicker** | `android.material: 'system'` | `Theme.AppCompat.*` | ✅ Yes |
|
|
773
|
-
| **SelectionMenu** | `android.material: 'm3'` | `Theme.Material3.*` | ✅ Yes |
|
|
774
|
-
| **SelectionMenu** | `android.material: 'system'` | `Theme.AppCompat.*` | ✅ Yes |
|
|
775
|
-
| **ContextMenu** | — | Any | ❌ No |
|
|
776
|
-
| **LiquidGlass** | — | Any | ❌ No |
|
|
777
|
-
|
|
778
|
-
### Material 3 Theme Setup (Required for SegmentedControl)
|
|
779
|
-
|
|
780
|
-
`SegmentedControl` always uses Material 3 widgets (`MaterialButtonToggleGroup`). Your app **must** use a Material 3 theme or the app will crash on component mount.
|
|
781
|
-
|
|
782
|
-
**1. Update your app theme in `android/app/src/main/res/values/styles.xml`:**
|
|
783
|
-
|
|
784
|
-
```xml
|
|
785
|
-
<resources>
|
|
786
|
-
<!-- Base application theme - MUST inherit from Material3 -->
|
|
787
|
-
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
|
788
|
-
<!-- Material 3 requires these color attributes -->
|
|
789
|
-
<item name="colorPrimary">@color/md_theme_primary</item>
|
|
790
|
-
<item name="colorOnPrimary">@color/md_theme_onPrimary</item>
|
|
791
|
-
<item name="colorPrimaryContainer">@color/md_theme_primaryContainer</item>
|
|
792
|
-
<item name="colorOnPrimaryContainer">@color/md_theme_onPrimaryContainer</item>
|
|
793
|
-
<item name="colorSecondary">@color/md_theme_secondary</item>
|
|
794
|
-
<item name="colorOnSecondary">@color/md_theme_onSecondary</item>
|
|
795
|
-
<item name="colorSecondaryContainer">@color/md_theme_secondaryContainer</item>
|
|
796
|
-
<item name="colorOnSecondaryContainer">@color/md_theme_onSecondaryContainer</item>
|
|
797
|
-
<item name="colorTertiary">@color/md_theme_tertiary</item>
|
|
798
|
-
<item name="colorOnTertiary">@color/md_theme_onTertiary</item>
|
|
799
|
-
<item name="colorBackground">@color/md_theme_background</item>
|
|
800
|
-
<item name="colorOnBackground">@color/md_theme_onBackground</item>
|
|
801
|
-
<item name="colorSurface">@color/md_theme_surface</item>
|
|
802
|
-
<item name="colorOnSurface">@color/md_theme_onSurface</item>
|
|
803
|
-
<item name="colorError">@color/md_theme_error</item>
|
|
804
|
-
<item name="colorOnError">@color/md_theme_onError</item>
|
|
805
|
-
</style>
|
|
806
|
-
</resources>
|
|
807
|
-
```
|
|
808
|
-
|
|
809
|
-
**2. Define your Material 3 colors in `android/app/src/main/res/values/colors.xml`:**
|
|
810
|
-
|
|
811
|
-
```xml
|
|
812
|
-
<resources>
|
|
813
|
-
<!-- Generate these using Material Theme Builder: https://m3.material.io/theme-builder -->
|
|
814
|
-
<color name="md_theme_primary">#6750A4</color>
|
|
815
|
-
<color name="md_theme_onPrimary">#FFFFFF</color>
|
|
816
|
-
<color name="md_theme_primaryContainer">#EADDFF</color>
|
|
817
|
-
<color name="md_theme_onPrimaryContainer">#21005D</color>
|
|
818
|
-
<color name="md_theme_secondary">#625B71</color>
|
|
819
|
-
<color name="md_theme_onSecondary">#FFFFFF</color>
|
|
820
|
-
<color name="md_theme_secondaryContainer">#E8DEF8</color>
|
|
821
|
-
<color name="md_theme_onSecondaryContainer">#1D192B</color>
|
|
822
|
-
<color name="md_theme_tertiary">#7D5260</color>
|
|
823
|
-
<color name="md_theme_onTertiary">#FFFFFF</color>
|
|
824
|
-
<color name="md_theme_background">#FFFBFE</color>
|
|
825
|
-
<color name="md_theme_onBackground">#1C1B1F</color>
|
|
826
|
-
<color name="md_theme_surface">#FFFBFE</color>
|
|
827
|
-
<color name="md_theme_onSurface">#1C1B1F</color>
|
|
828
|
-
<color name="md_theme_error">#B3261E</color>
|
|
829
|
-
<color name="md_theme_onError">#FFFFFF</color>
|
|
830
|
-
</resources>
|
|
831
|
-
```
|
|
832
|
-
|
|
833
|
-
> **Tip:** Use Google's [Material Theme Builder](https://m3.material.io/theme-builder) to generate a complete color scheme.
|
|
834
|
-
|
|
835
|
-
### Common Crash Scenarios
|
|
836
|
-
|
|
837
|
-
#### Crash: `Cannot find theme attribute materialButtonOutlinedStyle`
|
|
838
|
-
|
|
839
|
-
**Cause:** Using `SegmentedControl` without a Material 3 theme.
|
|
840
|
-
|
|
841
|
-
**Fix:** Update your theme to inherit from `Theme.Material3.*`:
|
|
842
|
-
|
|
843
|
-
```xml
|
|
844
|
-
<!-- Change this -->
|
|
845
|
-
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
|
846
|
-
|
|
847
|
-
<!-- To this -->
|
|
848
|
-
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
|
849
|
-
```
|
|
850
|
-
|
|
851
|
-
#### Crash: `You need to use a Theme.AppCompat theme`
|
|
852
|
-
|
|
853
|
-
**Cause:** Using `DatePicker` or `SelectionMenu` with `android.material: 'system'` while not extending AppCompat.
|
|
854
|
-
|
|
855
|
-
**Fix:** Ensure your theme inherits from `Theme.AppCompat.*` or `Theme.Material3.*` (which extends AppCompat), and your `MainActivity` extends `AppCompatActivity`.
|
|
856
|
-
|
|
857
|
-
### Mode Selection Guide
|
|
858
|
-
|
|
859
|
-
Choose the appropriate mode based on your app's theme:
|
|
860
|
-
|
|
861
|
-
```tsx
|
|
862
|
-
// If your app uses Theme.Material3.* (recommended)
|
|
863
|
-
<DatePicker android={{ material: 'm3' }} />
|
|
864
|
-
<SelectionMenu android={{ material: 'm3' }} />
|
|
865
|
-
<SegmentedControl /> // Always M3
|
|
866
|
-
|
|
867
|
-
// If your app uses Theme.AppCompat.*
|
|
868
|
-
<DatePicker android={{ material: 'system' }} />
|
|
869
|
-
<SelectionMenu android={{ material: 'system' }} />
|
|
870
|
-
// ⚠️ SegmentedControl will crash - upgrade to Material 3
|
|
871
|
-
```
|
|
872
|
-
|
|
873
|
-
### Expo Configuration
|
|
874
|
-
|
|
875
|
-
For Expo projects, the config plugin can configure your theme automatically. Add to `app.json`:
|
|
111
|
+
**Expo:** needs a dev client or EAS Build (native code, so not Expo Go). The config plugin sets up the Material 3 theme for you:
|
|
876
112
|
|
|
877
113
|
```json
|
|
878
114
|
{
|
|
@@ -880,106 +116,47 @@ For Expo projects, the config plugin can configure your theme automatically. Add
|
|
|
880
116
|
"plugins": [
|
|
881
117
|
[
|
|
882
118
|
"react-native-platform-components/app.plugin",
|
|
883
|
-
{
|
|
884
|
-
"android": {
|
|
885
|
-
"theme": "material3"
|
|
886
|
-
}
|
|
887
|
-
}
|
|
119
|
+
{ "android": { "theme": "material3" } }
|
|
888
120
|
]
|
|
889
121
|
]
|
|
890
122
|
}
|
|
891
123
|
}
|
|
892
124
|
```
|
|
893
125
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
## Icons
|
|
899
|
-
|
|
900
|
-
ContextMenu supports icons on menu items. Icons are specified by name and resolved differently on each platform.
|
|
901
|
-
|
|
902
|
-
### iOS
|
|
903
|
-
|
|
904
|
-
Use [SF Symbols](https://developer.apple.com/sf-symbols/) names. These are built into iOS and require no additional setup.
|
|
905
|
-
|
|
906
|
-
```tsx
|
|
907
|
-
// Common SF Symbols
|
|
908
|
-
image: 'doc.on.doc'; // Copy
|
|
909
|
-
image: 'square.and.arrow.up'; // Share
|
|
910
|
-
image: 'trash'; // Delete
|
|
911
|
-
image: 'pencil'; // Edit
|
|
912
|
-
image: 'checkmark.circle'; // Checkmark
|
|
913
|
-
```
|
|
914
|
-
|
|
915
|
-
Browse available symbols using Apple's SF Symbols app or [sfsymbols.com](https://sfsymbols.com).
|
|
916
|
-
|
|
917
|
-
### Android
|
|
918
|
-
|
|
919
|
-
Use drawable resource names from your app's `res/drawable` directory. You must add these resources yourself.
|
|
920
|
-
|
|
921
|
-
```tsx
|
|
922
|
-
// Reference drawable by name (without extension)
|
|
923
|
-
image: 'content_copy'; // res/drawable/content_copy.xml
|
|
924
|
-
image: 'share'; // res/drawable/share.xml
|
|
925
|
-
image: 'delete'; // res/drawable/delete.xml
|
|
126
|
+
```sh
|
|
127
|
+
npx expo install react-native-platform-components
|
|
128
|
+
npx expo prebuild
|
|
129
|
+
npx expo run:ios # or run:android
|
|
926
130
|
```
|
|
927
131
|
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
1. Create vector drawable XML files in `android/app/src/main/res/drawable/`
|
|
931
|
-
2. Use [Material Icons](https://fonts.google.com/icons) as a source — download SVG and convert to Android Vector Drawable
|
|
932
|
-
3. Name the file to match the `image` prop value (e.g., `content_copy.xml` for `image: 'content_copy'`)
|
|
132
|
+
The library is built for the New Architecture (Fabric + Codegen) and does not support the old architecture.
|
|
933
133
|
|
|
934
|
-
|
|
134
|
+
**Compatibility:**
|
|
935
135
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
android:fillColor="@android:color/white"
|
|
944
|
-
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
|
|
945
|
-
</vector>
|
|
946
|
-
```
|
|
136
|
+
| Platform | Minimum |
|
|
137
|
+
| ------------ | --------------------------------------------------------- |
|
|
138
|
+
| React Native | 0.81 with the New Architecture enabled |
|
|
139
|
+
| Expo SDK | 54, with a dev client or EAS Build (not Expo Go) |
|
|
140
|
+
| React | 19 |
|
|
141
|
+
| iOS | 13 (LiquidGlass needs iOS 26) |
|
|
142
|
+
| Android | API 24 (Android 7.0); Material Components 1.12 is bundled |
|
|
947
143
|
|
|
948
|
-
|
|
144
|
+
Full details: [Installation](https://jarx-concepts.github.io/react-native-platform-components/installation).
|
|
949
145
|
|
|
950
|
-
|
|
146
|
+
## Documentation
|
|
951
147
|
|
|
952
|
-
|
|
953
|
-
import { Platform } from 'react-native';
|
|
954
|
-
|
|
955
|
-
const actions = [
|
|
956
|
-
{
|
|
957
|
-
id: 'copy',
|
|
958
|
-
title: 'Copy',
|
|
959
|
-
image: Platform.OS === 'ios' ? 'doc.on.doc' : 'content_copy',
|
|
960
|
-
},
|
|
961
|
-
{
|
|
962
|
-
id: 'share',
|
|
963
|
-
title: 'Share',
|
|
964
|
-
image: Platform.OS === 'ios' ? 'square.and.arrow.up' : 'share',
|
|
965
|
-
},
|
|
966
|
-
{
|
|
967
|
-
id: 'delete',
|
|
968
|
-
title: 'Delete',
|
|
969
|
-
image: Platform.OS === 'ios' ? 'trash' : 'delete',
|
|
970
|
-
attributes: { destructive: true },
|
|
971
|
-
},
|
|
972
|
-
];
|
|
973
|
-
```
|
|
148
|
+
Everything else lives on the documentation site:
|
|
974
149
|
|
|
975
|
-
|
|
150
|
+
- [Quick Start](https://jarx-concepts.github.io/react-native-platform-components/quick-start): copy-paste examples for every component
|
|
151
|
+
- [DatePicker](https://jarx-concepts.github.io/react-native-platform-components/components/datepicker), [ContextMenu](https://jarx-concepts.github.io/react-native-platform-components/components/contextmenu), [SelectionMenu](https://jarx-concepts.github.io/react-native-platform-components/components/selectionmenu), [SegmentedControl](https://jarx-concepts.github.io/react-native-platform-components/components/segmentedcontrol), [LiquidGlass](https://jarx-concepts.github.io/react-native-platform-components/components/liquidglass): props, platform options and behavior
|
|
152
|
+
- [Android Theme Configuration](https://jarx-concepts.github.io/react-native-platform-components/guides/android-theme): Material 3 setup and what happens without it
|
|
153
|
+
- [Icons](https://jarx-concepts.github.io/react-native-platform-components/guides/icons): SF Symbols, drawables and images
|
|
154
|
+
- [Theming and Colors](https://jarx-concepts.github.io/react-native-platform-components/guides/theming): design philosophy, theming, accepted color formats
|
|
976
155
|
|
|
977
156
|
## Contributing
|
|
978
157
|
|
|
979
158
|
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
|
|
980
159
|
|
|
981
|
-
**Have a component request?** If there's a native UI component you'd like to see added, [open an issue](https://github.com/JarX-Concepts/react-native-platform-components/issues/new) describing the component and its native APIs on iOS and Android.
|
|
982
|
-
|
|
983
160
|
## License
|
|
984
161
|
|
|
985
162
|
MIT
|