react-native-platform-components 0.7.0 → 0.8.1
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 +238 -102
- package/android/src/main/java/com/platformcomponents/PCLiquidGlassView.kt +84 -0
- package/android/src/main/java/com/platformcomponents/PCLiquidGlassViewManager.kt +52 -0
- package/android/src/main/java/com/platformcomponents/PlatformComponentsPackage.kt +1 -0
- package/ios/PCLiquidGlass.h +10 -0
- package/ios/PCLiquidGlass.mm +140 -0
- package/ios/PCLiquidGlass.swift +354 -0
- package/ios/PCSelectionMenu.swift +1 -1
- package/lib/commonjs/LiquidGlass.js +72 -0
- package/lib/commonjs/LiquidGlass.js.map +1 -0
- package/lib/commonjs/LiquidGlassNativeComponent.ts +110 -0
- package/lib/commonjs/index.js +11 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/LiquidGlass.js +64 -0
- package/lib/module/LiquidGlass.js.map +1 -0
- package/lib/module/LiquidGlassNativeComponent.ts +110 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/commonjs/src/LiquidGlass.d.ts +96 -0
- package/lib/typescript/commonjs/src/LiquidGlass.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/LiquidGlassNativeComponent.d.ts +93 -0
- package/lib/typescript/commonjs/src/LiquidGlassNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +1 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/LiquidGlass.d.ts +96 -0
- package/lib/typescript/module/src/LiquidGlass.d.ts.map +1 -0
- package/lib/typescript/module/src/LiquidGlassNativeComponent.d.ts +93 -0
- package/lib/typescript/module/src/LiquidGlassNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +1 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/package.json +12 -4
- package/src/LiquidGlass.tsx +169 -0
- package/src/LiquidGlassNativeComponent.ts +110 -0
- package/src/index.tsx +1 -0
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>
|
|
@@ -40,6 +42,14 @@ This library focuses on **true native behavior**, not JavaScript re-implementati
|
|
|
40
42
|
<td><img src="https://raw.githubusercontent.com/JarX-Concepts/react-native-platform-components/main/assets/ios-segmentedcontrol.gif" height="550" /></td>
|
|
41
43
|
<td><img src="https://raw.githubusercontent.com/JarX-Concepts/react-native-platform-components/main/assets/android-segmentedcontrol.gif" height="550" /></td>
|
|
42
44
|
</tr>
|
|
45
|
+
<tr>
|
|
46
|
+
<td align="center"><strong>iOS LiquidGlass</strong></td>
|
|
47
|
+
<td align="center"><strong>Android LiquidGlass</strong></td>
|
|
48
|
+
</tr>
|
|
49
|
+
<tr>
|
|
50
|
+
<td><img src="https://raw.githubusercontent.com/JarX-Concepts/react-native-platform-components/main/assets/ios-liquidglass.gif" height="550" /></td>
|
|
51
|
+
<td align="center"><em>iOS 26+ only</em><br/><br/>On Android, renders as a<br/>regular View with optional<br/>fallback background color.</td>
|
|
52
|
+
</tr>
|
|
43
53
|
</table>
|
|
44
54
|
|
|
45
55
|
### Components
|
|
@@ -48,6 +58,7 @@ This library focuses on **true native behavior**, not JavaScript re-implementati
|
|
|
48
58
|
- **ContextMenu** – native context menus with long-press activation (UIContextMenuInteraction on iOS, PopupMenu on Android)
|
|
49
59
|
- **SelectionMenu** – native selection menus (Material on Android, system menus on iOS)
|
|
50
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)
|
|
51
62
|
|
|
52
63
|
### Goals
|
|
53
64
|
|
|
@@ -106,12 +117,11 @@ eas build --platform android
|
|
|
106
117
|
**Config Plugin:**
|
|
107
118
|
|
|
108
119
|
Add to your `app.json`:
|
|
120
|
+
|
|
109
121
|
```json
|
|
110
122
|
{
|
|
111
123
|
"expo": {
|
|
112
|
-
"plugins": [
|
|
113
|
-
["react-native-platform-components/app.plugin", {}]
|
|
114
|
-
]
|
|
124
|
+
"plugins": [["react-native-platform-components/app.plugin", {}]]
|
|
115
125
|
}
|
|
116
126
|
}
|
|
117
127
|
```
|
|
@@ -124,18 +134,20 @@ For a complete working example, see the [`example-expo/`](./example-expo) direct
|
|
|
124
134
|
|
|
125
135
|
This library is built for the **React Native New Architecture** (Fabric + TurboModules).
|
|
126
136
|
|
|
127
|
-
| Feature
|
|
128
|
-
|
|
129
|
-
| Fabric (New Renderer) | Supported
|
|
130
|
-
| Codegen
|
|
131
|
-
| TurboModules
|
|
132
|
-
| 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 |
|
|
133
143
|
|
|
134
144
|
**Tested with:**
|
|
145
|
+
|
|
135
146
|
- React Native 0.81+ (bare and Expo)
|
|
136
147
|
- Expo SDK 54+
|
|
137
148
|
|
|
138
149
|
**Requirements:**
|
|
150
|
+
|
|
139
151
|
- New Architecture must be enabled in your app
|
|
140
152
|
- For bare React Native: set `newArchEnabled=true` in `gradle.properties` (Android) and use the `RCT_NEW_ARCH_ENABLED` flag (iOS)
|
|
141
153
|
- For Expo: set `"newArchEnabled": true` in `app.json`
|
|
@@ -167,8 +179,8 @@ export function Example() {
|
|
|
167
179
|
setVisible(false);
|
|
168
180
|
}}
|
|
169
181
|
onClosed={() => setVisible(false)}
|
|
170
|
-
ios={{preferredStyle: 'inline'}}
|
|
171
|
-
android={{material: 'system'}}
|
|
182
|
+
ios={{ preferredStyle: 'inline' }}
|
|
183
|
+
android={{ material: 'system' }}
|
|
172
184
|
/>
|
|
173
185
|
</>
|
|
174
186
|
);
|
|
@@ -189,8 +201,8 @@ export function Example() {
|
|
|
189
201
|
presentation="embedded"
|
|
190
202
|
mode="date"
|
|
191
203
|
onConfirm={(d) => setDate(d)}
|
|
192
|
-
ios={{preferredStyle: 'inline'}}
|
|
193
|
-
android={{material: 'system'}}
|
|
204
|
+
ios={{ preferredStyle: 'inline' }}
|
|
205
|
+
android={{ material: 'system' }}
|
|
194
206
|
/>
|
|
195
207
|
);
|
|
196
208
|
}
|
|
@@ -230,7 +242,9 @@ export function Example() {
|
|
|
230
242
|
]}
|
|
231
243
|
onPressAction={(id, title) => setLastAction(title)}
|
|
232
244
|
>
|
|
233
|
-
<View
|
|
245
|
+
<View
|
|
246
|
+
style={{ padding: 20, backgroundColor: '#E8F4FD', borderRadius: 8 }}
|
|
247
|
+
>
|
|
234
248
|
<Text>Long-press me</Text>
|
|
235
249
|
</View>
|
|
236
250
|
</ContextMenu>
|
|
@@ -388,6 +402,54 @@ export function Example() {
|
|
|
388
402
|
|
|
389
403
|
---
|
|
390
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
|
+
|
|
391
453
|
## Components
|
|
392
454
|
|
|
393
455
|
## DatePicker
|
|
@@ -396,37 +458,37 @@ Native date & time picker using **platform system pickers**.
|
|
|
396
458
|
|
|
397
459
|
### Props
|
|
398
460
|
|
|
399
|
-
| Prop
|
|
400
|
-
|
|
401
|
-
| `date`
|
|
402
|
-
| `minDate`
|
|
403
|
-
| `maxDate`
|
|
404
|
-
| `locale`
|
|
405
|
-
| `timeZoneName` | `string`
|
|
406
|
-
| `mode`
|
|
407
|
-
| `presentation` | `'modal' \| 'embedded'`
|
|
408
|
-
| `visible`
|
|
409
|
-
| `onConfirm`
|
|
410
|
-
| `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 |
|
|
411
473
|
|
|
412
474
|
### iOS Props (`ios`)
|
|
413
475
|
|
|
414
|
-
| Prop
|
|
415
|
-
|
|
416
|
-
| `preferredStyle`
|
|
417
|
-
| `countDownDurationSeconds` | `number`
|
|
418
|
-
| `minuteInterval`
|
|
419
|
-
| `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 |
|
|
420
482
|
|
|
421
483
|
### Android Props (`android`)
|
|
422
484
|
|
|
423
|
-
| Prop
|
|
424
|
-
|
|
425
|
-
| `firstDayOfWeek`
|
|
426
|
-
| `material`
|
|
427
|
-
| `dialogTitle`
|
|
428
|
-
| `positiveButtonTitle` | `string`
|
|
429
|
-
| `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 |
|
|
430
492
|
|
|
431
493
|
---
|
|
432
494
|
|
|
@@ -436,42 +498,42 @@ Native context menu that wraps content and responds to **long-press** or **tap**
|
|
|
436
498
|
|
|
437
499
|
### Props
|
|
438
500
|
|
|
439
|
-
| Prop
|
|
440
|
-
|
|
441
|
-
| `title`
|
|
442
|
-
| `actions`
|
|
443
|
-
| `disabled`
|
|
444
|
-
| `trigger`
|
|
445
|
-
| `onPressAction` | `(actionId, actionTitle) => void` | Called when user selects an action
|
|
446
|
-
| `onMenuOpen`
|
|
447
|
-
| `onMenuClose`
|
|
448
|
-
| `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) |
|
|
449
511
|
|
|
450
512
|
### ContextMenuAction
|
|
451
513
|
|
|
452
|
-
| Property
|
|
453
|
-
|
|
454
|
-
| `id`
|
|
455
|
-
| `title`
|
|
456
|
-
| `subtitle`
|
|
457
|
-
| `image`
|
|
458
|
-
| `imageColor` | `string`
|
|
459
|
-
| `attributes` | `{ destructive?, disabled?, hidden? }` | Action attributes
|
|
460
|
-
| `state`
|
|
461
|
-
| `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 |
|
|
462
524
|
|
|
463
525
|
### iOS Props (`ios`)
|
|
464
526
|
|
|
465
|
-
| Prop
|
|
466
|
-
|
|
527
|
+
| Prop | Type | Description |
|
|
528
|
+
| --------------- | --------- | --------------------------------- |
|
|
467
529
|
| `enablePreview` | `boolean` | Enable preview when long-pressing |
|
|
468
530
|
|
|
469
531
|
### Android Props (`android`)
|
|
470
532
|
|
|
471
|
-
| Prop
|
|
472
|
-
|
|
473
|
-
| `anchorPosition` | `'left' \| 'right'` | Anchor position for the popup menu
|
|
474
|
-
| `visible`
|
|
533
|
+
| Prop | Type | Description |
|
|
534
|
+
| ---------------- | ------------------- | ---------------------------------------------- |
|
|
535
|
+
| `anchorPosition` | `'left' \| 'right'` | Anchor position for the popup menu |
|
|
536
|
+
| `visible` | `boolean` | Programmatic visibility control (Android only) |
|
|
475
537
|
|
|
476
538
|
### Trigger Modes
|
|
477
539
|
|
|
@@ -492,17 +554,17 @@ Native selection menu with **modal** and **embedded** modes.
|
|
|
492
554
|
|
|
493
555
|
### Props
|
|
494
556
|
|
|
495
|
-
| Prop
|
|
496
|
-
|
|
497
|
-
| `options`
|
|
498
|
-
| `selected`
|
|
499
|
-
| `disabled`
|
|
500
|
-
| `placeholder`
|
|
501
|
-
| `presentation`
|
|
502
|
-
| `visible`
|
|
503
|
-
| `onSelect`
|
|
504
|
-
| `onRequestClose`
|
|
505
|
-
| `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 |
|
|
506
568
|
|
|
507
569
|
### Modes
|
|
508
570
|
|
|
@@ -519,44 +581,106 @@ Native segmented control using **UISegmentedControl** on iOS and **MaterialButto
|
|
|
519
581
|
|
|
520
582
|
### Props
|
|
521
583
|
|
|
522
|
-
| Prop
|
|
523
|
-
|
|
524
|
-
| `segments`
|
|
525
|
-
| `selectedValue` | `string \| null`
|
|
526
|
-
| `disabled`
|
|
527
|
-
| `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 |
|
|
528
590
|
|
|
529
591
|
### SegmentedControlSegment
|
|
530
592
|
|
|
531
|
-
| Property
|
|
532
|
-
|
|
533
|
-
| `label`
|
|
534
|
-
| `value`
|
|
535
|
-
| `disabled` | `boolean` | Disables this specific segment
|
|
536
|
-
| `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) |
|
|
537
599
|
|
|
538
600
|
### iOS Props (`ios`)
|
|
539
601
|
|
|
540
|
-
| Prop
|
|
541
|
-
|
|
542
|
-
| `momentary`
|
|
602
|
+
| Prop | Type | Description |
|
|
603
|
+
| ---------------------------------- | --------- | --------------------------------------------------- |
|
|
604
|
+
| `momentary` | `boolean` | If true, segments don't show selected state |
|
|
543
605
|
| `apportionsSegmentWidthsByContent` | `boolean` | If true, segment widths are proportional to content |
|
|
544
|
-
| `selectedSegmentTintColor`
|
|
606
|
+
| `selectedSegmentTintColor` | `string` | Tint color for selected segment (hex string) |
|
|
545
607
|
|
|
546
608
|
### Android Props (`android`)
|
|
547
609
|
|
|
548
|
-
| Prop
|
|
549
|
-
|
|
610
|
+
| Prop | Type | Description |
|
|
611
|
+
| ------------------- | --------- | -------------------------------------------- |
|
|
550
612
|
| `selectionRequired` | `boolean` | If true, one segment must always be selected |
|
|
551
613
|
|
|
552
614
|
### Icon Support
|
|
553
615
|
|
|
554
616
|
Icons work the same as ContextMenu:
|
|
617
|
+
|
|
555
618
|
- **iOS**: Use SF Symbol names (e.g., `'list.bullet'`, `'square.grid.2x2'`)
|
|
556
619
|
- **Android**: Use drawable resource names (e.g., `'list_bullet'`, `'grid_view'`)
|
|
557
620
|
|
|
558
621
|
---
|
|
559
622
|
|
|
623
|
+
## LiquidGlass
|
|
624
|
+
|
|
625
|
+
Native glass morphism effect using **UIGlassEffect** on iOS 26+. On Android and older iOS versions, renders as a regular View with optional fallback styling.
|
|
626
|
+
|
|
627
|
+
> **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.
|
|
628
|
+
|
|
629
|
+
### Props
|
|
630
|
+
|
|
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 |
|
|
635
|
+
|
|
636
|
+
### iOS Props (`ios`)
|
|
637
|
+
|
|
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 |
|
|
646
|
+
|
|
647
|
+
### Android Props (`android`)
|
|
648
|
+
|
|
649
|
+
| Prop | Type | Description |
|
|
650
|
+
| ------------------------- | -------- | ---------------------------------------------- |
|
|
651
|
+
| `fallbackBackgroundColor` | `string` | Background color when glass effect unavailable |
|
|
652
|
+
|
|
653
|
+
### Constants
|
|
654
|
+
|
|
655
|
+
| Export | Type | Description |
|
|
656
|
+
| ------------------------ | --------- | ------------------------------------ |
|
|
657
|
+
| `isLiquidGlassSupported` | `boolean` | `true` on iOS 26+, `false` otherwise |
|
|
658
|
+
|
|
659
|
+
### Effect Modes
|
|
660
|
+
|
|
661
|
+
- **`'regular'`** (default): Standard glass blur intensity with full glass morphism effect
|
|
662
|
+
- **`'clear'`**: More transparent, subtle glass effect
|
|
663
|
+
- **`'none'`**: No glass effect (useful for animating materialization/dematerialization)
|
|
664
|
+
|
|
665
|
+
### Platform Behavior
|
|
666
|
+
|
|
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 |
|
|
674
|
+
|
|
675
|
+
### Usage Tips
|
|
676
|
+
|
|
677
|
+
1. **Check support first**: Use `isLiquidGlassSupported` to conditionally render fallback UI
|
|
678
|
+
2. **Background content**: Glass effects work best over images or colorful backgrounds
|
|
679
|
+
3. **Interactive mode**: Only applies on mount; cannot be toggled after initial render
|
|
680
|
+
4. **Android fallback**: Set `android.fallbackBackgroundColor` for a semi-transparent background
|
|
681
|
+
|
|
682
|
+
---
|
|
683
|
+
|
|
560
684
|
## Design Philosophy
|
|
561
685
|
|
|
562
686
|
- **Native first** — no JS re-implementation of pickers
|
|
@@ -588,11 +712,11 @@ Use [SF Symbols](https://developer.apple.com/sf-symbols/) names. These are built
|
|
|
588
712
|
|
|
589
713
|
```tsx
|
|
590
714
|
// Common SF Symbols
|
|
591
|
-
image: 'doc.on.doc'
|
|
592
|
-
image: 'square.and.arrow.up' // Share
|
|
593
|
-
image: 'trash'
|
|
594
|
-
image: 'pencil'
|
|
595
|
-
image: 'checkmark.circle'
|
|
715
|
+
image: 'doc.on.doc'; // Copy
|
|
716
|
+
image: 'square.and.arrow.up'; // Share
|
|
717
|
+
image: 'trash'; // Delete
|
|
718
|
+
image: 'pencil'; // Edit
|
|
719
|
+
image: 'checkmark.circle'; // Checkmark
|
|
596
720
|
```
|
|
597
721
|
|
|
598
722
|
Browse available symbols using Apple's SF Symbols app or [sfsymbols.com](https://sfsymbols.com).
|
|
@@ -603,9 +727,9 @@ Use drawable resource names from your app's `res/drawable` directory. You must a
|
|
|
603
727
|
|
|
604
728
|
```tsx
|
|
605
729
|
// Reference drawable by name (without extension)
|
|
606
|
-
image: 'content_copy'
|
|
607
|
-
image: 'share'
|
|
608
|
-
image: 'delete'
|
|
730
|
+
image: 'content_copy'; // res/drawable/content_copy.xml
|
|
731
|
+
image: 'share'; // res/drawable/share.xml
|
|
732
|
+
image: 'delete'; // res/drawable/delete.xml
|
|
609
733
|
```
|
|
610
734
|
|
|
611
735
|
**Adding drawable resources:**
|
|
@@ -666,3 +790,15 @@ See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the
|
|
|
666
790
|
## License
|
|
667
791
|
|
|
668
792
|
MIT
|
|
793
|
+
|
|
794
|
+
---
|
|
795
|
+
|
|
796
|
+
## Author
|
|
797
|
+
|
|
798
|
+
**Andrew Tosh** — Santa Barbara, California
|
|
799
|
+
|
|
800
|
+
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.
|
|
801
|
+
|
|
802
|
+
**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).
|
|
803
|
+
|
|
804
|
+
[LinkedIn](https://www.linkedin.com/in/atosh/) · [JarX Concepts](https://github.com/JarX-Concepts)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
package com.platformcomponents
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import android.graphics.drawable.GradientDrawable
|
|
6
|
+
import android.view.View
|
|
7
|
+
import android.widget.FrameLayout
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Android stub implementation for LiquidGlass.
|
|
11
|
+
*
|
|
12
|
+
* LiquidGlass is an iOS 26+ only feature. On Android, this component renders
|
|
13
|
+
* as a regular FrameLayout with optional fallback styling (background color, corner radius).
|
|
14
|
+
*/
|
|
15
|
+
class PCLiquidGlassView(context: Context) : FrameLayout(context) {
|
|
16
|
+
|
|
17
|
+
companion object {
|
|
18
|
+
private const val TAG = "PCLiquidGlass"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// --- Props ---
|
|
22
|
+
var cornerRadius: Float = 0f
|
|
23
|
+
set(value) {
|
|
24
|
+
field = value
|
|
25
|
+
updateBackground()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var fallbackBackgroundColor: String? = null
|
|
29
|
+
set(value) {
|
|
30
|
+
field = value
|
|
31
|
+
updateBackground()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
init {
|
|
35
|
+
// Ensure children can be rendered
|
|
36
|
+
clipChildren = false
|
|
37
|
+
clipToPadding = false
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private fun updateBackground() {
|
|
41
|
+
val bgColor = fallbackBackgroundColor?.let { parseColor(it) }
|
|
42
|
+
|
|
43
|
+
if (cornerRadius > 0 || bgColor != null) {
|
|
44
|
+
val drawable = GradientDrawable().apply {
|
|
45
|
+
shape = GradientDrawable.RECTANGLE
|
|
46
|
+
cornerRadii = FloatArray(8) { cornerRadius * resources.displayMetrics.density }
|
|
47
|
+
setColor(bgColor ?: Color.TRANSPARENT)
|
|
48
|
+
}
|
|
49
|
+
background = drawable
|
|
50
|
+
clipToOutline = cornerRadius > 0
|
|
51
|
+
outlineProvider = android.view.ViewOutlineProvider.BACKGROUND
|
|
52
|
+
} else {
|
|
53
|
+
background = null
|
|
54
|
+
clipToOutline = false
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private fun parseColor(colorString: String): Int? {
|
|
59
|
+
return try {
|
|
60
|
+
var sanitized = colorString.trim()
|
|
61
|
+
if (!sanitized.startsWith("#")) {
|
|
62
|
+
sanitized = "#$sanitized"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Handle #RRGGBBAA format (web/CSS style) by converting to #AARRGGBB (Android style)
|
|
66
|
+
if (sanitized.length == 9) {
|
|
67
|
+
val rrggbb = sanitized.substring(1, 7)
|
|
68
|
+
val aa = sanitized.substring(7, 9)
|
|
69
|
+
sanitized = "#$aa$rrggbb"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Color.parseColor(sanitized)
|
|
73
|
+
} catch (e: Exception) {
|
|
74
|
+
null
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ---- Measurement ----
|
|
79
|
+
|
|
80
|
+
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
81
|
+
// Standard FrameLayout measurement
|
|
82
|
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
package com.platformcomponents
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReadableMap
|
|
4
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
5
|
+
import com.facebook.react.uimanager.ViewGroupManager
|
|
6
|
+
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
7
|
+
import com.facebook.react.viewmanagers.PCLiquidGlassManagerDelegate
|
|
8
|
+
import com.facebook.react.viewmanagers.PCLiquidGlassManagerInterface
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Android ViewManager for LiquidGlass.
|
|
12
|
+
*
|
|
13
|
+
* LiquidGlass is iOS-only, so this manager provides a stub implementation
|
|
14
|
+
* that renders a basic FrameLayout with optional fallback styling.
|
|
15
|
+
* Uses ViewGroupManager since LiquidGlass can contain children.
|
|
16
|
+
*/
|
|
17
|
+
class PCLiquidGlassViewManager :
|
|
18
|
+
ViewGroupManager<PCLiquidGlassView>(),
|
|
19
|
+
PCLiquidGlassManagerInterface<PCLiquidGlassView> {
|
|
20
|
+
|
|
21
|
+
companion object {
|
|
22
|
+
private const val TAG = "PCLiquidGlass"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private val delegate: ViewManagerDelegate<PCLiquidGlassView> =
|
|
26
|
+
PCLiquidGlassManagerDelegate(this)
|
|
27
|
+
|
|
28
|
+
override fun getName(): String = "PCLiquidGlass"
|
|
29
|
+
|
|
30
|
+
override fun getDelegate(): ViewManagerDelegate<PCLiquidGlassView> = delegate
|
|
31
|
+
|
|
32
|
+
override fun createViewInstance(reactContext: ThemedReactContext): PCLiquidGlassView {
|
|
33
|
+
return PCLiquidGlassView(reactContext)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
override fun setCornerRadius(view: PCLiquidGlassView, value: Float) {
|
|
37
|
+
view.cornerRadius = value
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
override fun setIos(view: PCLiquidGlassView, value: ReadableMap?) {
|
|
41
|
+
// iOS props are ignored on Android
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override fun setAndroid(view: PCLiquidGlassView, value: ReadableMap?) {
|
|
45
|
+
val fallbackColor = value?.let {
|
|
46
|
+
if (it.hasKey("fallbackBackgroundColor") && !it.isNull("fallbackBackgroundColor")) {
|
|
47
|
+
it.getString("fallbackBackgroundColor")
|
|
48
|
+
} else null
|
|
49
|
+
}
|
|
50
|
+
view.fallbackBackgroundColor = fallbackColor
|
|
51
|
+
}
|
|
52
|
+
}
|