react-native-platform-components 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/android/build.gradle +3 -1
- package/android/src/main/java/com/platformcomponents/PCConstants.kt +3 -0
- package/android/src/main/java/com/platformcomponents/PCDatePickerView.kt +53 -1
- package/android/src/main/java/com/platformcomponents/PCDatePickerViewManager.kt +14 -0
- package/android/src/main/java/com/platformcomponents/PCSelectionMenuView.kt +169 -10
- package/android/src/main/java/com/platformcomponents/PCSelectionMenuViewManager.kt +14 -0
- package/android/src/main/jni/CMakeLists.txt +47 -0
- package/android/src/main/jni/OnLoad.cpp +33 -0
- package/ios/PCDatePickerView.swift +19 -2
- package/ios/PCSelectionMenu.mm +42 -0
- package/ios/PCSelectionMenu.swift +17 -0
- package/lib/module/SelectionMenu.js +1 -8
- package/lib/module/SelectionMenu.js.map +1 -1
- package/lib/typescript/src/SelectionMenu.d.ts.map +1 -1
- package/package.json +1 -1
- package/react-native.config.js +13 -0
- package/shared/PCDatePickerComponentDescriptors-custom.h +14 -43
- package/shared/PCDatePickerShadowNode-custom.cpp +35 -0
- package/shared/PCDatePickerShadowNode-custom.h +40 -18
- package/shared/PCDatePickerState-custom.h +53 -1
- package/shared/PCSelectionMenuComponentDescriptors-custom.h +15 -18
- package/shared/PCSelectionMenuShadowNode-custom.cpp +42 -21
- package/shared/PCSelectionMenuShadowNode-custom.h +23 -10
- package/shared/PCSelectionMenuState-custom.h +65 -0
- package/shared/README.md +179 -0
- package/shared/react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h +9 -0
- package/src/SelectionMenu.tsx +2 -12
package/shared/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Shared C++ Code for Yoga Shadow Node Measurement
|
|
2
|
+
|
|
3
|
+
This directory contains cross-platform C++ code that enables native components to report their measured sizes to React Native's Yoga layout engine. This allows components like `DatePicker` and `SelectionMenu` to have "perfect" sizing based on their actual native content, rather than relying on hardcoded dimensions.
|
|
4
|
+
|
|
5
|
+
## Architecture Overview
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
9
|
+
│ React Native (JS) │
|
|
10
|
+
│ <DatePicker /> <SelectionMenu /> │
|
|
11
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
12
|
+
│
|
|
13
|
+
▼
|
|
14
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
15
|
+
│ Yoga Layout Engine (C++) │
|
|
16
|
+
│ │
|
|
17
|
+
│ Calls measureContent() on shadow nodes to determine intrinsic size │
|
|
18
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
19
|
+
│
|
|
20
|
+
▼
|
|
21
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
22
|
+
│ Custom Shadow Nodes (this directory) │
|
|
23
|
+
│ │
|
|
24
|
+
│ MeasuringPCDatePickerShadowNode │
|
|
25
|
+
│ MeasuringPCSelectionMenuShadowNode │
|
|
26
|
+
│ │
|
|
27
|
+
│ - Marked as LeafYogaNode + MeasurableYogaNode │
|
|
28
|
+
│ - Override measureContent() to return size from state │
|
|
29
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
30
|
+
│
|
|
31
|
+
▼
|
|
32
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
33
|
+
│ Custom State (this directory) │
|
|
34
|
+
│ │
|
|
35
|
+
│ PCDatePickerStateFrameSize { Size frameSize } │
|
|
36
|
+
│ PCSelectionMenuStateFrameSize { Size frameSize } │
|
|
37
|
+
│ │
|
|
38
|
+
│ - Holds measured dimensions from native │
|
|
39
|
+
│ - Supports serialization for Android (folly::dynamic) │
|
|
40
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
41
|
+
▲
|
|
42
|
+
│
|
|
43
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
44
|
+
│ Native Views (iOS/Android) │
|
|
45
|
+
│ │
|
|
46
|
+
│ iOS: PCDatePickerView, PCSelectionMenuView │
|
|
47
|
+
│ Android: PCDatePickerView, PCSelectionMenuView │
|
|
48
|
+
│ │
|
|
49
|
+
│ - Measure actual native content │
|
|
50
|
+
│ - Call state->updateState() with measured frameSize │
|
|
51
|
+
│ - Triggers Yoga re-layout with correct dimensions │
|
|
52
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## How It Works
|
|
56
|
+
|
|
57
|
+
### 1. Shadow Node Traits
|
|
58
|
+
|
|
59
|
+
Shadow nodes are marked with two critical traits:
|
|
60
|
+
|
|
61
|
+
```cpp
|
|
62
|
+
static ShadowNodeTraits BaseTraits() {
|
|
63
|
+
auto traits = ConcreteViewShadowNode::BaseTraits();
|
|
64
|
+
traits.set(ShadowNodeTraits::Trait::LeafYogaNode); // No Yoga children
|
|
65
|
+
traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode); // Has measureContent()
|
|
66
|
+
return traits;
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- **LeafYogaNode**: Tells Yoga this node has no children to layout
|
|
71
|
+
- **MeasurableYogaNode**: Tells Yoga to call `measureContent()` for intrinsic sizing
|
|
72
|
+
|
|
73
|
+
### 2. Measurement Flow
|
|
74
|
+
|
|
75
|
+
```cpp
|
|
76
|
+
Size measureContent(
|
|
77
|
+
const LayoutContext& layoutContext,
|
|
78
|
+
const LayoutConstraints& layoutConstraints) const override {
|
|
79
|
+
|
|
80
|
+
// Get the measured size from native (via state)
|
|
81
|
+
const auto& stateData = this->getStateData();
|
|
82
|
+
Float measuredW = stateData.frameSize.width;
|
|
83
|
+
Float measuredH = stateData.frameSize.height;
|
|
84
|
+
|
|
85
|
+
// Apply layout constraints and return
|
|
86
|
+
return layoutConstraints.clamp(Size{measuredW, measuredH});
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. Native → Shadow Node Communication (State)
|
|
91
|
+
|
|
92
|
+
Native views measure their content and update the shadow node's state:
|
|
93
|
+
|
|
94
|
+
**iOS (Objective-C++):**
|
|
95
|
+
```objc
|
|
96
|
+
- (void)updateMeasurements {
|
|
97
|
+
CGSize size = [_nativeView sizeForLayoutWithConstrainedTo:...];
|
|
98
|
+
|
|
99
|
+
PCDatePickerStateFrameSize next;
|
|
100
|
+
next.frameSize = {(Float)size.width, (Float)size.height};
|
|
101
|
+
_state->updateState(std::move(next));
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Android (Kotlin + JNI):**
|
|
106
|
+
```kotlin
|
|
107
|
+
private fun updateMeasurements(width: Float, height: Float) {
|
|
108
|
+
updateState(width, height) // JNI call to C++
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 4. State Serialization (Android)
|
|
113
|
+
|
|
114
|
+
Android requires state to be serializable via `folly::dynamic`:
|
|
115
|
+
|
|
116
|
+
```cpp
|
|
117
|
+
struct PCDatePickerStateFrameSize {
|
|
118
|
+
#ifdef RN_SERIALIZABLE_STATE
|
|
119
|
+
// Constructor from dynamic data
|
|
120
|
+
PCDatePickerStateFrameSize(
|
|
121
|
+
const PCDatePickerStateFrameSize& previousState,
|
|
122
|
+
folly::dynamic data) {
|
|
123
|
+
if (data.isObject() && data.count("width") && data.count("height")) {
|
|
124
|
+
frameSize.width = static_cast<Float>(data["width"].asDouble());
|
|
125
|
+
frameSize.height = static_cast<Float>(data["height"].asDouble());
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
folly::dynamic getDynamic() const {
|
|
130
|
+
return folly::dynamic::object("width", frameSize.width)("height", frameSize.height);
|
|
131
|
+
}
|
|
132
|
+
#endif
|
|
133
|
+
};
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## File Structure
|
|
137
|
+
|
|
138
|
+
| File | Purpose |
|
|
139
|
+
|------|---------|
|
|
140
|
+
| `PC*ShadowNode-custom.h` | Shadow node class declaration with `measureContent()` |
|
|
141
|
+
| `PC*ShadowNode-custom.cpp` | `measureContent()` implementation |
|
|
142
|
+
| `PC*State-custom.h` | State struct holding `frameSize` from native |
|
|
143
|
+
| `PC*ComponentDescriptors-custom.h` | Type alias for component descriptor using custom shadow node |
|
|
144
|
+
|
|
145
|
+
## Fallback Behavior
|
|
146
|
+
|
|
147
|
+
When native hasn't yet reported measurements (state is empty), the shadow nodes use platform-specific fallback heights:
|
|
148
|
+
|
|
149
|
+
```cpp
|
|
150
|
+
// SelectionMenu fallbacks
|
|
151
|
+
static constexpr float kFallbackHeightIOS = 44.0f; // iOS standard row
|
|
152
|
+
static constexpr float kFallbackHeightAndroid = 56.0f; // Android Spinner
|
|
153
|
+
static constexpr float kFallbackHeightAndroidM3 = 72.0f; // Android M3 TextInputLayout
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
This prevents layout jumps on initial render before native measurement completes.
|
|
157
|
+
|
|
158
|
+
## Integration Points
|
|
159
|
+
|
|
160
|
+
### iOS
|
|
161
|
+
|
|
162
|
+
- `ios/PCDatePicker.mm` - Calls `updateMeasurements` when props change
|
|
163
|
+
- `ios/PCSelectionMenu.mm` - Calls `updateMeasurements` when props change
|
|
164
|
+
|
|
165
|
+
### Android
|
|
166
|
+
|
|
167
|
+
- `android/.../PCDatePickerView.kt` - Calls JNI `updateState()` after measure
|
|
168
|
+
- `android/.../PCSelectionMenuView.kt` - Calls JNI `updateState()` after measure
|
|
169
|
+
- `android/src/main/jni/OnLoad.cpp` - JNI bridge for state updates
|
|
170
|
+
|
|
171
|
+
## Common Pitfalls
|
|
172
|
+
|
|
173
|
+
1. **Circular includes**: Shadow node headers should NOT include ComponentDescriptors.h. Use forward declarations instead.
|
|
174
|
+
|
|
175
|
+
2. **State timing**: Native measurement may happen after initial Yoga layout. Always provide sensible fallback values.
|
|
176
|
+
|
|
177
|
+
3. **Width constraints**: When width isn't measured (0), use `layoutConstraints.maximumSize.width` as the width.
|
|
178
|
+
|
|
179
|
+
4. **Android serialization**: The `RN_SERIALIZABLE_STATE` macro gates Android-specific serialization code.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
// Include the codegen-generated component descriptors using include_next
|
|
4
|
+
// This allows us to shadow the header while still including the original
|
|
5
|
+
#include_next <react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h>
|
|
6
|
+
|
|
7
|
+
// Include our custom component descriptors which use measuring shadow nodes
|
|
8
|
+
#include "PCSelectionMenuComponentDescriptors-custom.h"
|
|
9
|
+
#include "PCDatePickerComponentDescriptors-custom.h"
|
package/src/SelectionMenu.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SelectionMenu.tsx
|
|
2
2
|
import React, { useCallback, useMemo } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { type ViewProps } from 'react-native';
|
|
4
4
|
|
|
5
5
|
import NativeSelectionMenu, {
|
|
6
6
|
type SelectionMenuOption,
|
|
@@ -116,15 +116,9 @@ export function SelectionMenu(props: SelectionMenuProps): React.ReactElement {
|
|
|
116
116
|
return { material: android.material };
|
|
117
117
|
}, [android]);
|
|
118
118
|
|
|
119
|
-
const isAndroidM3Inline =
|
|
120
|
-
android?.material &&
|
|
121
|
-
inlineMode &&
|
|
122
|
-
android.material === 'm3' &&
|
|
123
|
-
Platform.OS === 'android';
|
|
124
|
-
|
|
125
119
|
return (
|
|
126
120
|
<NativeSelectionMenu
|
|
127
|
-
style={
|
|
121
|
+
style={style}
|
|
128
122
|
options={options}
|
|
129
123
|
selectedData={selectedData}
|
|
130
124
|
interactivity={disabled ? 'disabled' : 'enabled'}
|
|
@@ -139,7 +133,3 @@ export function SelectionMenu(props: SelectionMenuProps): React.ReactElement {
|
|
|
139
133
|
/>
|
|
140
134
|
);
|
|
141
135
|
}
|
|
142
|
-
|
|
143
|
-
const styles = StyleSheet.create({
|
|
144
|
-
androidInline: { minHeight: 60 },
|
|
145
|
-
});
|