react-native-platform-components 0.8.0 → 0.8.2
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 +295 -129
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@ High-quality **native UI components for React Native**, implemented with platfor
|
|
|
7
7
|
|
|
8
8
|
This library focuses on **true native behavior**, not JavaScript re-implementations.
|
|
9
9
|
|
|
10
|
+
**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.
|
|
11
|
+
|
|
10
12
|
<table>
|
|
11
13
|
<tr>
|
|
12
14
|
<td align="center"><strong>iOS DatePicker</strong></td>
|
|
@@ -89,7 +91,7 @@ pod install
|
|
|
89
91
|
|
|
90
92
|
- Uses native Android Views with Material Design (including `PopupMenu` for context menus)
|
|
91
93
|
- Supports **Material 3** styling
|
|
92
|
-
-
|
|
94
|
+
- **⚠️ Your app may crash if theme is not configured** — See [Android Theme Configuration](#android-theme-configuration) below
|
|
93
95
|
|
|
94
96
|
### Expo (Managed Workflow)
|
|
95
97
|
|
|
@@ -115,12 +117,11 @@ eas build --platform android
|
|
|
115
117
|
**Config Plugin:**
|
|
116
118
|
|
|
117
119
|
Add to your `app.json`:
|
|
120
|
+
|
|
118
121
|
```json
|
|
119
122
|
{
|
|
120
123
|
"expo": {
|
|
121
|
-
"plugins": [
|
|
122
|
-
["react-native-platform-components/app.plugin", {}]
|
|
123
|
-
]
|
|
124
|
+
"plugins": [["react-native-platform-components/app.plugin", {}]]
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
127
|
```
|
|
@@ -133,18 +134,20 @@ For a complete working example, see the [`example-expo/`](./example-expo) direct
|
|
|
133
134
|
|
|
134
135
|
This library is built for the **React Native New Architecture** (Fabric + TurboModules).
|
|
135
136
|
|
|
136
|
-
| Feature
|
|
137
|
-
|
|
138
|
-
| Fabric (New Renderer) | Supported
|
|
139
|
-
| Codegen
|
|
140
|
-
| TurboModules
|
|
141
|
-
| Old Architecture
|
|
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 |
|
|
142
143
|
|
|
143
144
|
**Tested with:**
|
|
145
|
+
|
|
144
146
|
- React Native 0.81+ (bare and Expo)
|
|
145
147
|
- Expo SDK 54+
|
|
146
148
|
|
|
147
149
|
**Requirements:**
|
|
150
|
+
|
|
148
151
|
- New Architecture must be enabled in your app
|
|
149
152
|
- For bare React Native: set `newArchEnabled=true` in `gradle.properties` (Android) and use the `RCT_NEW_ARCH_ENABLED` flag (iOS)
|
|
150
153
|
- For Expo: set `"newArchEnabled": true` in `app.json`
|
|
@@ -176,8 +179,8 @@ export function Example() {
|
|
|
176
179
|
setVisible(false);
|
|
177
180
|
}}
|
|
178
181
|
onClosed={() => setVisible(false)}
|
|
179
|
-
ios={{preferredStyle: 'inline'}}
|
|
180
|
-
android={{material: 'system'}}
|
|
182
|
+
ios={{ preferredStyle: 'inline' }}
|
|
183
|
+
android={{ material: 'system' }}
|
|
181
184
|
/>
|
|
182
185
|
</>
|
|
183
186
|
);
|
|
@@ -198,8 +201,8 @@ export function Example() {
|
|
|
198
201
|
presentation="embedded"
|
|
199
202
|
mode="date"
|
|
200
203
|
onConfirm={(d) => setDate(d)}
|
|
201
|
-
ios={{preferredStyle: 'inline'}}
|
|
202
|
-
android={{material: 'system'}}
|
|
204
|
+
ios={{ preferredStyle: 'inline' }}
|
|
205
|
+
android={{ material: 'system' }}
|
|
203
206
|
/>
|
|
204
207
|
);
|
|
205
208
|
}
|
|
@@ -239,7 +242,9 @@ export function Example() {
|
|
|
239
242
|
]}
|
|
240
243
|
onPressAction={(id, title) => setLastAction(title)}
|
|
241
244
|
>
|
|
242
|
-
<View
|
|
245
|
+
<View
|
|
246
|
+
style={{ padding: 20, backgroundColor: '#E8F4FD', borderRadius: 8 }}
|
|
247
|
+
>
|
|
243
248
|
<Text>Long-press me</Text>
|
|
244
249
|
</View>
|
|
245
250
|
</ContextMenu>
|
|
@@ -400,18 +405,30 @@ export function Example() {
|
|
|
400
405
|
### LiquidGlass
|
|
401
406
|
|
|
402
407
|
```tsx
|
|
403
|
-
import {
|
|
408
|
+
import {
|
|
409
|
+
LiquidGlass,
|
|
410
|
+
isLiquidGlassSupported,
|
|
411
|
+
} from 'react-native-platform-components';
|
|
404
412
|
import { View, Text, Image } from 'react-native';
|
|
405
413
|
|
|
406
414
|
export function Example() {
|
|
407
415
|
return (
|
|
408
416
|
<View style={{ flex: 1 }}>
|
|
409
417
|
{/* Background content */}
|
|
410
|
-
<Image
|
|
418
|
+
<Image
|
|
419
|
+
source={{ uri: 'https://example.com/photo.jpg' }}
|
|
420
|
+
style={{ flex: 1 }}
|
|
421
|
+
/>
|
|
411
422
|
|
|
412
423
|
{/* Glass effect overlay */}
|
|
413
424
|
<LiquidGlass
|
|
414
|
-
style={{
|
|
425
|
+
style={{
|
|
426
|
+
position: 'absolute',
|
|
427
|
+
top: 50,
|
|
428
|
+
left: 20,
|
|
429
|
+
right: 20,
|
|
430
|
+
padding: 20,
|
|
431
|
+
}}
|
|
415
432
|
cornerRadius={20}
|
|
416
433
|
ios={{
|
|
417
434
|
effect: 'regular',
|
|
@@ -441,37 +458,37 @@ Native date & time picker using **platform system pickers**.
|
|
|
441
458
|
|
|
442
459
|
### Props
|
|
443
460
|
|
|
444
|
-
| Prop
|
|
445
|
-
|
|
446
|
-
| `date`
|
|
447
|
-
| `minDate`
|
|
448
|
-
| `maxDate`
|
|
449
|
-
| `locale`
|
|
450
|
-
| `timeZoneName` | `string`
|
|
451
|
-
| `mode`
|
|
452
|
-
| `presentation` | `'modal' \| 'embedded'`
|
|
453
|
-
| `visible`
|
|
454
|
-
| `onConfirm`
|
|
455
|
-
| `onClosed`
|
|
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) => void` | Called when user confirms selection |
|
|
472
|
+
| `onClosed` | `() => void` | Called when modal is dismissed |
|
|
456
473
|
|
|
457
474
|
### iOS Props (`ios`)
|
|
458
475
|
|
|
459
|
-
| Prop
|
|
460
|
-
|
|
461
|
-
| `preferredStyle`
|
|
462
|
-
| `countDownDurationSeconds` | `number`
|
|
463
|
-
| `minuteInterval`
|
|
464
|
-
| `roundsToMinuteInterval`
|
|
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 |
|
|
465
482
|
|
|
466
483
|
### Android Props (`android`)
|
|
467
484
|
|
|
468
|
-
| Prop
|
|
469
|
-
|
|
470
|
-
| `firstDayOfWeek`
|
|
471
|
-
| `material`
|
|
472
|
-
| `dialogTitle`
|
|
473
|
-
| `positiveButtonTitle` | `string`
|
|
474
|
-
| `negativeButtonTitle` | `string`
|
|
485
|
+
| Prop | Type | Description |
|
|
486
|
+
| --------------------- | ------------------ | ---------------------------------------------------------------------- |
|
|
487
|
+
| `firstDayOfWeek` | `number` | First day of week (1-7, Sunday=1) |
|
|
488
|
+
| `material` | `'system' \| 'm3'` | Material Design style (modal only; embedded always uses system picker) |
|
|
489
|
+
| `dialogTitle` | `string` | Custom dialog title |
|
|
490
|
+
| `positiveButtonTitle` | `string` | Custom confirm button text |
|
|
491
|
+
| `negativeButtonTitle` | `string` | Custom cancel button text |
|
|
475
492
|
|
|
476
493
|
---
|
|
477
494
|
|
|
@@ -481,42 +498,42 @@ Native context menu that wraps content and responds to **long-press** or **tap**
|
|
|
481
498
|
|
|
482
499
|
### Props
|
|
483
500
|
|
|
484
|
-
| Prop
|
|
485
|
-
|
|
486
|
-
| `title`
|
|
487
|
-
| `actions`
|
|
488
|
-
| `disabled`
|
|
489
|
-
| `trigger`
|
|
490
|
-
| `onPressAction` | `(actionId, actionTitle) => void` | Called when user selects an action
|
|
491
|
-
| `onMenuOpen`
|
|
492
|
-
| `onMenuClose`
|
|
493
|
-
| `children`
|
|
501
|
+
| Prop | Type | Description |
|
|
502
|
+
| --------------- | --------------------------------- | ------------------------------------------- |
|
|
503
|
+
| `title` | `string` | Menu title (shown as header on iOS) |
|
|
504
|
+
| `actions` | `ContextMenuAction[]` | Array of menu actions |
|
|
505
|
+
| `disabled` | `boolean` | Disables the menu |
|
|
506
|
+
| `trigger` | `'longPress' \| 'tap'` | How the menu opens (default: `'longPress'`) |
|
|
507
|
+
| `onPressAction` | `(actionId, actionTitle) => void` | Called when user selects an action |
|
|
508
|
+
| `onMenuOpen` | `() => void` | Called when menu opens |
|
|
509
|
+
| `onMenuClose` | `() => void` | Called when menu closes |
|
|
510
|
+
| `children` | `ReactNode` | Content to wrap (required) |
|
|
494
511
|
|
|
495
512
|
### ContextMenuAction
|
|
496
513
|
|
|
497
|
-
| Property
|
|
498
|
-
|
|
499
|
-
| `id`
|
|
500
|
-
| `title`
|
|
501
|
-
| `subtitle`
|
|
502
|
-
| `image`
|
|
503
|
-
| `imageColor` | `string`
|
|
504
|
-
| `attributes` | `{ destructive?, disabled?, hidden? }` | Action attributes
|
|
505
|
-
| `state`
|
|
506
|
-
| `subactions` | `ContextMenuAction[]`
|
|
514
|
+
| Property | Type | Description |
|
|
515
|
+
| ------------ | -------------------------------------- | ------------------------------------------------- |
|
|
516
|
+
| `id` | `string` | Unique identifier returned in callbacks |
|
|
517
|
+
| `title` | `string` | Display text |
|
|
518
|
+
| `subtitle` | `string` | Secondary text (iOS only) |
|
|
519
|
+
| `image` | `string` | Icon name (SF Symbol on iOS, drawable on Android) |
|
|
520
|
+
| `imageColor` | `string` | Tint color for the icon (hex string) |
|
|
521
|
+
| `attributes` | `{ destructive?, disabled?, hidden? }` | Action attributes |
|
|
522
|
+
| `state` | `'off' \| 'on' \| 'mixed'` | Checkmark state |
|
|
523
|
+
| `subactions` | `ContextMenuAction[]` | Nested actions for submenu |
|
|
507
524
|
|
|
508
525
|
### iOS Props (`ios`)
|
|
509
526
|
|
|
510
|
-
| Prop
|
|
511
|
-
|
|
527
|
+
| Prop | Type | Description |
|
|
528
|
+
| --------------- | --------- | --------------------------------- |
|
|
512
529
|
| `enablePreview` | `boolean` | Enable preview when long-pressing |
|
|
513
530
|
|
|
514
531
|
### Android Props (`android`)
|
|
515
532
|
|
|
516
|
-
| Prop
|
|
517
|
-
|
|
518
|
-
| `anchorPosition` | `'left' \| 'right'` | Anchor position for the popup menu
|
|
519
|
-
| `visible`
|
|
533
|
+
| Prop | Type | Description |
|
|
534
|
+
| ---------------- | ------------------- | ---------------------------------------------- |
|
|
535
|
+
| `anchorPosition` | `'left' \| 'right'` | Anchor position for the popup menu |
|
|
536
|
+
| `visible` | `boolean` | Programmatic visibility control (Android only) |
|
|
520
537
|
|
|
521
538
|
### Trigger Modes
|
|
522
539
|
|
|
@@ -537,17 +554,17 @@ Native selection menu with **modal** and **embedded** modes.
|
|
|
537
554
|
|
|
538
555
|
### Props
|
|
539
556
|
|
|
540
|
-
| Prop
|
|
541
|
-
|
|
542
|
-
| `options`
|
|
543
|
-
| `selected`
|
|
544
|
-
| `disabled`
|
|
545
|
-
| `placeholder`
|
|
546
|
-
| `presentation`
|
|
547
|
-
| `visible`
|
|
548
|
-
| `onSelect`
|
|
549
|
-
| `onRequestClose`
|
|
550
|
-
| `android.material` | `'system' \| 'm3'`
|
|
557
|
+
| Prop | Type | Description |
|
|
558
|
+
| ------------------ | ----------------------------------- | ----------------------------------------------- |
|
|
559
|
+
| `options` | `{ label: string; data: string }[]` | Array of options to display |
|
|
560
|
+
| `selected` | `string \| null` | Currently selected option's `data` value |
|
|
561
|
+
| `disabled` | `boolean` | Disables the menu |
|
|
562
|
+
| `placeholder` | `string` | Placeholder text when no selection |
|
|
563
|
+
| `presentation` | `'modal' \| 'embedded'` | Presentation mode (default: `'modal'`) |
|
|
564
|
+
| `visible` | `boolean` | Controls modal mode menu visibility |
|
|
565
|
+
| `onSelect` | `(data, label, index) => void` | Called when user selects an option |
|
|
566
|
+
| `onRequestClose` | `() => void` | Called when menu is dismissed without selection |
|
|
567
|
+
| `android.material` | `'system' \| 'm3'` | Material Design style preference |
|
|
551
568
|
|
|
552
569
|
### Modes
|
|
553
570
|
|
|
@@ -564,39 +581,40 @@ Native segmented control using **UISegmentedControl** on iOS and **MaterialButto
|
|
|
564
581
|
|
|
565
582
|
### Props
|
|
566
583
|
|
|
567
|
-
| Prop
|
|
568
|
-
|
|
569
|
-
| `segments`
|
|
570
|
-
| `selectedValue` | `string \| null`
|
|
571
|
-
| `disabled`
|
|
572
|
-
| `onSelect`
|
|
584
|
+
| Prop | Type | Description |
|
|
585
|
+
| --------------- | ---------------------------------------- | ------------------------------------ |
|
|
586
|
+
| `segments` | `SegmentedControlSegment[]` | Array of segments to display |
|
|
587
|
+
| `selectedValue` | `string \| null` | Currently selected segment's `value` |
|
|
588
|
+
| `disabled` | `boolean` | Disables the entire control |
|
|
589
|
+
| `onSelect` | `(value: string, index: number) => void` | Called when user selects a segment |
|
|
573
590
|
|
|
574
591
|
### SegmentedControlSegment
|
|
575
592
|
|
|
576
|
-
| Property
|
|
577
|
-
|
|
578
|
-
| `label`
|
|
579
|
-
| `value`
|
|
580
|
-
| `disabled` | `boolean` | Disables this specific segment
|
|
581
|
-
| `icon`
|
|
593
|
+
| Property | Type | Description |
|
|
594
|
+
| ---------- | --------- | ------------------------------------------------- |
|
|
595
|
+
| `label` | `string` | Display text for the segment |
|
|
596
|
+
| `value` | `string` | Unique value returned in callbacks |
|
|
597
|
+
| `disabled` | `boolean` | Disables this specific segment |
|
|
598
|
+
| `icon` | `string` | Icon name (SF Symbol on iOS, drawable on Android) |
|
|
582
599
|
|
|
583
600
|
### iOS Props (`ios`)
|
|
584
601
|
|
|
585
|
-
| Prop
|
|
586
|
-
|
|
587
|
-
| `momentary`
|
|
602
|
+
| Prop | Type | Description |
|
|
603
|
+
| ---------------------------------- | --------- | --------------------------------------------------- |
|
|
604
|
+
| `momentary` | `boolean` | If true, segments don't show selected state |
|
|
588
605
|
| `apportionsSegmentWidthsByContent` | `boolean` | If true, segment widths are proportional to content |
|
|
589
|
-
| `selectedSegmentTintColor`
|
|
606
|
+
| `selectedSegmentTintColor` | `string` | Tint color for selected segment (hex string) |
|
|
590
607
|
|
|
591
608
|
### Android Props (`android`)
|
|
592
609
|
|
|
593
|
-
| Prop
|
|
594
|
-
|
|
610
|
+
| Prop | Type | Description |
|
|
611
|
+
| ------------------- | --------- | -------------------------------------------- |
|
|
595
612
|
| `selectionRequired` | `boolean` | If true, one segment must always be selected |
|
|
596
613
|
|
|
597
614
|
### Icon Support
|
|
598
615
|
|
|
599
616
|
Icons work the same as ContextMenu:
|
|
617
|
+
|
|
600
618
|
- **iOS**: Use SF Symbol names (e.g., `'list.bullet'`, `'square.grid.2x2'`)
|
|
601
619
|
- **Android**: Use drawable resource names (e.g., `'list_bullet'`, `'grid_view'`)
|
|
602
620
|
|
|
@@ -610,32 +628,32 @@ Native glass morphism effect using **UIGlassEffect** on iOS 26+. On Android and
|
|
|
610
628
|
|
|
611
629
|
### Props
|
|
612
630
|
|
|
613
|
-
| Prop
|
|
614
|
-
|
|
615
|
-
| `cornerRadius` | `number`
|
|
616
|
-
| `children`
|
|
631
|
+
| Prop | Type | Description |
|
|
632
|
+
| -------------- | ----------- | ------------------------------------------------- |
|
|
633
|
+
| `cornerRadius` | `number` | Corner radius for the glass effect (default: `0`) |
|
|
634
|
+
| `children` | `ReactNode` | Content to render inside the glass container |
|
|
617
635
|
|
|
618
636
|
### iOS Props (`ios`)
|
|
619
637
|
|
|
620
|
-
| Prop
|
|
621
|
-
|
|
622
|
-
| `effect`
|
|
623
|
-
| `interactive`
|
|
624
|
-
| `tintColor`
|
|
625
|
-
| `colorScheme`
|
|
626
|
-
| `shadowRadius`
|
|
627
|
-
| `isHighlighted` | `boolean`
|
|
638
|
+
| Prop | Type | Description |
|
|
639
|
+
| --------------- | -------------------------------- | ---------------------------------------------------- |
|
|
640
|
+
| `effect` | `'clear' \| 'regular' \| 'none'` | Glass effect intensity (default: `'regular'`) |
|
|
641
|
+
| `interactive` | `boolean` | Enable touch interaction feedback (default: `false`) |
|
|
642
|
+
| `tintColor` | `string` | Overlay tint color (hex string) |
|
|
643
|
+
| `colorScheme` | `'light' \| 'dark' \| 'system'` | Appearance mode (default: `'system'`) |
|
|
644
|
+
| `shadowRadius` | `number` | Shadow/glow radius (default: `20`) |
|
|
645
|
+
| `isHighlighted` | `boolean` | Manual highlight state control |
|
|
628
646
|
|
|
629
647
|
### Android Props (`android`)
|
|
630
648
|
|
|
631
|
-
| Prop
|
|
632
|
-
|
|
649
|
+
| Prop | Type | Description |
|
|
650
|
+
| ------------------------- | -------- | ---------------------------------------------- |
|
|
633
651
|
| `fallbackBackgroundColor` | `string` | Background color when glass effect unavailable |
|
|
634
652
|
|
|
635
653
|
### Constants
|
|
636
654
|
|
|
637
|
-
| Export
|
|
638
|
-
|
|
655
|
+
| Export | Type | Description |
|
|
656
|
+
| ------------------------ | --------- | ------------------------------------ |
|
|
639
657
|
| `isLiquidGlassSupported` | `boolean` | `true` on iOS 26+, `false` otherwise |
|
|
640
658
|
|
|
641
659
|
### Effect Modes
|
|
@@ -646,13 +664,13 @@ Native glass morphism effect using **UIGlassEffect** on iOS 26+. On Android and
|
|
|
646
664
|
|
|
647
665
|
### Platform Behavior
|
|
648
666
|
|
|
649
|
-
| Platform
|
|
650
|
-
|
|
651
|
-
| Glass Effect
|
|
652
|
-
| Corner Radius | Applied
|
|
653
|
-
| Tint Color
|
|
654
|
-
| Interactive
|
|
655
|
-
| Fallback BG
|
|
667
|
+
| Platform | iOS 26+ | iOS < 26 | Android |
|
|
668
|
+
| ------------- | ------------------- | ----------- | ------------ |
|
|
669
|
+
| Glass Effect | Full glass morphism | No effect | No effect |
|
|
670
|
+
| Corner Radius | Applied | Applied | Applied |
|
|
671
|
+
| Tint Color | Supported | Ignored | Ignored |
|
|
672
|
+
| Interactive | Supported | Ignored | Ignored |
|
|
673
|
+
| Fallback BG | N/A | Transparent | Configurable |
|
|
656
674
|
|
|
657
675
|
### Usage Tips
|
|
658
676
|
|
|
@@ -684,6 +702,142 @@ This is intentional. The goal is native fidelity, not pixel-level customization.
|
|
|
684
702
|
|
|
685
703
|
---
|
|
686
704
|
|
|
705
|
+
## Android Theme Configuration
|
|
706
|
+
|
|
707
|
+
> **⚠️ 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.
|
|
708
|
+
|
|
709
|
+
### Theme Requirements by Component
|
|
710
|
+
|
|
711
|
+
| Component | Mode | Required Theme | Crash if Missing |
|
|
712
|
+
| -------------------- | ---------------------------- | ------------------- | ---------------- |
|
|
713
|
+
| **SegmentedControl** | (always M3) | `Theme.Material3.*` | ✅ Yes |
|
|
714
|
+
| **DatePicker** | `android.material: 'm3'` | `Theme.Material3.*` | ✅ Yes |
|
|
715
|
+
| **DatePicker** | `android.material: 'system'` | `Theme.AppCompat.*` | ✅ Yes |
|
|
716
|
+
| **SelectionMenu** | `android.material: 'm3'` | `Theme.Material3.*` | ✅ Yes |
|
|
717
|
+
| **SelectionMenu** | `android.material: 'system'` | `Theme.AppCompat.*` | ✅ Yes |
|
|
718
|
+
| **ContextMenu** | — | Any | ❌ No |
|
|
719
|
+
| **LiquidGlass** | — | Any | ❌ No |
|
|
720
|
+
|
|
721
|
+
### Material 3 Theme Setup (Required for SegmentedControl)
|
|
722
|
+
|
|
723
|
+
`SegmentedControl` always uses Material 3 widgets (`MaterialButtonToggleGroup`). Your app **must** use a Material 3 theme or the app will crash on component mount.
|
|
724
|
+
|
|
725
|
+
**1. Update your app theme in `android/app/src/main/res/values/styles.xml`:**
|
|
726
|
+
|
|
727
|
+
```xml
|
|
728
|
+
<resources>
|
|
729
|
+
<!-- Base application theme - MUST inherit from Material3 -->
|
|
730
|
+
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
|
731
|
+
<!-- Material 3 requires these color attributes -->
|
|
732
|
+
<item name="colorPrimary">@color/md_theme_primary</item>
|
|
733
|
+
<item name="colorOnPrimary">@color/md_theme_onPrimary</item>
|
|
734
|
+
<item name="colorPrimaryContainer">@color/md_theme_primaryContainer</item>
|
|
735
|
+
<item name="colorOnPrimaryContainer">@color/md_theme_onPrimaryContainer</item>
|
|
736
|
+
<item name="colorSecondary">@color/md_theme_secondary</item>
|
|
737
|
+
<item name="colorOnSecondary">@color/md_theme_onSecondary</item>
|
|
738
|
+
<item name="colorSecondaryContainer">@color/md_theme_secondaryContainer</item>
|
|
739
|
+
<item name="colorOnSecondaryContainer">@color/md_theme_onSecondaryContainer</item>
|
|
740
|
+
<item name="colorTertiary">@color/md_theme_tertiary</item>
|
|
741
|
+
<item name="colorOnTertiary">@color/md_theme_onTertiary</item>
|
|
742
|
+
<item name="colorBackground">@color/md_theme_background</item>
|
|
743
|
+
<item name="colorOnBackground">@color/md_theme_onBackground</item>
|
|
744
|
+
<item name="colorSurface">@color/md_theme_surface</item>
|
|
745
|
+
<item name="colorOnSurface">@color/md_theme_onSurface</item>
|
|
746
|
+
<item name="colorError">@color/md_theme_error</item>
|
|
747
|
+
<item name="colorOnError">@color/md_theme_onError</item>
|
|
748
|
+
</style>
|
|
749
|
+
</resources>
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
**2. Define your Material 3 colors in `android/app/src/main/res/values/colors.xml`:**
|
|
753
|
+
|
|
754
|
+
```xml
|
|
755
|
+
<resources>
|
|
756
|
+
<!-- Generate these using Material Theme Builder: https://m3.material.io/theme-builder -->
|
|
757
|
+
<color name="md_theme_primary">#6750A4</color>
|
|
758
|
+
<color name="md_theme_onPrimary">#FFFFFF</color>
|
|
759
|
+
<color name="md_theme_primaryContainer">#EADDFF</color>
|
|
760
|
+
<color name="md_theme_onPrimaryContainer">#21005D</color>
|
|
761
|
+
<color name="md_theme_secondary">#625B71</color>
|
|
762
|
+
<color name="md_theme_onSecondary">#FFFFFF</color>
|
|
763
|
+
<color name="md_theme_secondaryContainer">#E8DEF8</color>
|
|
764
|
+
<color name="md_theme_onSecondaryContainer">#1D192B</color>
|
|
765
|
+
<color name="md_theme_tertiary">#7D5260</color>
|
|
766
|
+
<color name="md_theme_onTertiary">#FFFFFF</color>
|
|
767
|
+
<color name="md_theme_background">#FFFBFE</color>
|
|
768
|
+
<color name="md_theme_onBackground">#1C1B1F</color>
|
|
769
|
+
<color name="md_theme_surface">#FFFBFE</color>
|
|
770
|
+
<color name="md_theme_onSurface">#1C1B1F</color>
|
|
771
|
+
<color name="md_theme_error">#B3261E</color>
|
|
772
|
+
<color name="md_theme_onError">#FFFFFF</color>
|
|
773
|
+
</resources>
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
> **Tip:** Use Google's [Material Theme Builder](https://m3.material.io/theme-builder) to generate a complete color scheme.
|
|
777
|
+
|
|
778
|
+
### Common Crash Scenarios
|
|
779
|
+
|
|
780
|
+
#### Crash: `Cannot find theme attribute materialButtonOutlinedStyle`
|
|
781
|
+
|
|
782
|
+
**Cause:** Using `SegmentedControl` without a Material 3 theme.
|
|
783
|
+
|
|
784
|
+
**Fix:** Update your theme to inherit from `Theme.Material3.*`:
|
|
785
|
+
|
|
786
|
+
```xml
|
|
787
|
+
<!-- Change this -->
|
|
788
|
+
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
|
789
|
+
|
|
790
|
+
<!-- To this -->
|
|
791
|
+
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
#### Crash: `You need to use a Theme.AppCompat theme`
|
|
795
|
+
|
|
796
|
+
**Cause:** Using `DatePicker` or `SelectionMenu` with `android.material: 'system'` while not extending AppCompat.
|
|
797
|
+
|
|
798
|
+
**Fix:** Ensure your theme inherits from `Theme.AppCompat.*` or `Theme.Material3.*` (which extends AppCompat), and your `MainActivity` extends `AppCompatActivity`.
|
|
799
|
+
|
|
800
|
+
### Mode Selection Guide
|
|
801
|
+
|
|
802
|
+
Choose the appropriate mode based on your app's theme:
|
|
803
|
+
|
|
804
|
+
```tsx
|
|
805
|
+
// If your app uses Theme.Material3.* (recommended)
|
|
806
|
+
<DatePicker android={{ material: 'm3' }} />
|
|
807
|
+
<SelectionMenu android={{ material: 'm3' }} />
|
|
808
|
+
<SegmentedControl /> // Always M3
|
|
809
|
+
|
|
810
|
+
// If your app uses Theme.AppCompat.*
|
|
811
|
+
<DatePicker android={{ material: 'system' }} />
|
|
812
|
+
<SelectionMenu android={{ material: 'system' }} />
|
|
813
|
+
// ⚠️ SegmentedControl will crash - upgrade to Material 3
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
### Expo Configuration
|
|
817
|
+
|
|
818
|
+
For Expo projects, the config plugin can configure your theme automatically. Add to `app.json`:
|
|
819
|
+
|
|
820
|
+
```json
|
|
821
|
+
{
|
|
822
|
+
"expo": {
|
|
823
|
+
"plugins": [
|
|
824
|
+
[
|
|
825
|
+
"react-native-platform-components/app.plugin",
|
|
826
|
+
{
|
|
827
|
+
"android": {
|
|
828
|
+
"theme": "material3"
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
]
|
|
832
|
+
]
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
Then run `npx expo prebuild` to apply the configuration.
|
|
838
|
+
|
|
839
|
+
---
|
|
840
|
+
|
|
687
841
|
## Icons
|
|
688
842
|
|
|
689
843
|
ContextMenu supports icons on menu items. Icons are specified by name and resolved differently on each platform.
|
|
@@ -694,11 +848,11 @@ Use [SF Symbols](https://developer.apple.com/sf-symbols/) names. These are built
|
|
|
694
848
|
|
|
695
849
|
```tsx
|
|
696
850
|
// Common SF Symbols
|
|
697
|
-
image: 'doc.on.doc'
|
|
698
|
-
image: 'square.and.arrow.up' // Share
|
|
699
|
-
image: 'trash'
|
|
700
|
-
image: 'pencil'
|
|
701
|
-
image: 'checkmark.circle'
|
|
851
|
+
image: 'doc.on.doc'; // Copy
|
|
852
|
+
image: 'square.and.arrow.up'; // Share
|
|
853
|
+
image: 'trash'; // Delete
|
|
854
|
+
image: 'pencil'; // Edit
|
|
855
|
+
image: 'checkmark.circle'; // Checkmark
|
|
702
856
|
```
|
|
703
857
|
|
|
704
858
|
Browse available symbols using Apple's SF Symbols app or [sfsymbols.com](https://sfsymbols.com).
|
|
@@ -709,9 +863,9 @@ Use drawable resource names from your app's `res/drawable` directory. You must a
|
|
|
709
863
|
|
|
710
864
|
```tsx
|
|
711
865
|
// Reference drawable by name (without extension)
|
|
712
|
-
image: 'content_copy'
|
|
713
|
-
image: 'share'
|
|
714
|
-
image: 'delete'
|
|
866
|
+
image: 'content_copy'; // res/drawable/content_copy.xml
|
|
867
|
+
image: 'share'; // res/drawable/share.xml
|
|
868
|
+
image: 'delete'; // res/drawable/delete.xml
|
|
715
869
|
```
|
|
716
870
|
|
|
717
871
|
**Adding drawable resources:**
|
|
@@ -772,3 +926,15 @@ See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the
|
|
|
772
926
|
## License
|
|
773
927
|
|
|
774
928
|
MIT
|
|
929
|
+
|
|
930
|
+
---
|
|
931
|
+
|
|
932
|
+
## Author
|
|
933
|
+
|
|
934
|
+
**Andrew Tosh** — Santa Barbara, California
|
|
935
|
+
|
|
936
|
+
Full-stack developer with deep experience across entertainment and defense industries, specializing in simulation, visualization, and cross-platform mobile development. Technical focus areas include React Native, Rust, C++, real-time 3D visualization, and game engine technologies.
|
|
937
|
+
|
|
938
|
+
**Available for contract work** — Always interested in connecting with new clients for mobile development, visualization systems, and related projects. Hit me up at [andrew.tosh@jarxconcepts.com](mailto:andrew.tosh@jarxconcepts.com).
|
|
939
|
+
|
|
940
|
+
[LinkedIn](https://www.linkedin.com/in/atosh/) · [JarX Concepts](https://github.com/JarX-Concepts)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-platform-components",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Native UI components for React Native: DatePicker, ContextMenu, SelectionMenu, SegmentedControl, LiquidGlass.",
|
|
5
5
|
"main": "./lib/commonjs/index.js",
|
|
6
6
|
"module": "./lib/module/index.js",
|