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.
Files changed (28) hide show
  1. package/README.md +6 -1
  2. package/android/build.gradle +3 -1
  3. package/android/src/main/java/com/platformcomponents/PCConstants.kt +3 -0
  4. package/android/src/main/java/com/platformcomponents/PCDatePickerView.kt +53 -1
  5. package/android/src/main/java/com/platformcomponents/PCDatePickerViewManager.kt +14 -0
  6. package/android/src/main/java/com/platformcomponents/PCSelectionMenuView.kt +169 -10
  7. package/android/src/main/java/com/platformcomponents/PCSelectionMenuViewManager.kt +14 -0
  8. package/android/src/main/jni/CMakeLists.txt +47 -0
  9. package/android/src/main/jni/OnLoad.cpp +33 -0
  10. package/ios/PCDatePickerView.swift +19 -2
  11. package/ios/PCSelectionMenu.mm +42 -0
  12. package/ios/PCSelectionMenu.swift +17 -0
  13. package/lib/module/SelectionMenu.js +1 -8
  14. package/lib/module/SelectionMenu.js.map +1 -1
  15. package/lib/typescript/src/SelectionMenu.d.ts.map +1 -1
  16. package/package.json +1 -1
  17. package/react-native.config.js +13 -0
  18. package/shared/PCDatePickerComponentDescriptors-custom.h +14 -43
  19. package/shared/PCDatePickerShadowNode-custom.cpp +35 -0
  20. package/shared/PCDatePickerShadowNode-custom.h +40 -18
  21. package/shared/PCDatePickerState-custom.h +53 -1
  22. package/shared/PCSelectionMenuComponentDescriptors-custom.h +15 -18
  23. package/shared/PCSelectionMenuShadowNode-custom.cpp +42 -21
  24. package/shared/PCSelectionMenuShadowNode-custom.h +23 -10
  25. package/shared/PCSelectionMenuState-custom.h +65 -0
  26. package/shared/README.md +179 -0
  27. package/shared/react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h +9 -0
  28. package/src/SelectionMenu.tsx +2 -12
@@ -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"
@@ -1,6 +1,6 @@
1
1
  // SelectionMenu.tsx
2
2
  import React, { useCallback, useMemo } from 'react';
3
- import { Platform, StyleSheet, type ViewProps } from 'react-native';
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={[style, isAndroidM3Inline && styles.androidInline]}
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
- });