react-native-platform-components 0.5.3 → 0.5.5
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 +13 -8
- 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 +58 -2
- package/ios/PCSelectionMenu.mm +42 -0
- package/ios/PCSelectionMenu.swift +17 -0
- package/lib/module/SelectionMenu.js +7 -14
- package/lib/module/SelectionMenu.js.map +1 -1
- package/lib/typescript/src/SelectionMenu.d.ts +6 -5
- package/lib/typescript/src/SelectionMenu.d.ts.map +1 -1
- package/lib/typescript/src/sharedTypes.d.ts +3 -1
- package/lib/typescript/src/sharedTypes.d.ts.map +1 -1
- package/package.json +3 -2
- 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 +15 -24
- package/src/sharedTypes.ts +4 -1
package/ios/PCSelectionMenu.mm
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#import <react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h>
|
|
10
10
|
#import <react/renderer/components/PlatformComponentsViewSpec/EventEmitters.h>
|
|
11
11
|
#import <react/renderer/components/PlatformComponentsViewSpec/Props.h>
|
|
12
|
+
#import <react/renderer/core/LayoutPrimitives.h>
|
|
12
13
|
|
|
13
14
|
#if __has_include(<PlatformComponents/PlatformComponents-Swift.h>)
|
|
14
15
|
#import <PlatformComponents/PlatformComponents-Swift.h>
|
|
@@ -17,6 +18,8 @@
|
|
|
17
18
|
#endif
|
|
18
19
|
|
|
19
20
|
#import "PCSelectionMenuComponentDescriptors-custom.h"
|
|
21
|
+
#import "PCSelectionMenuShadowNode-custom.h"
|
|
22
|
+
#import "PCSelectionMenuState-custom.h"
|
|
20
23
|
|
|
21
24
|
using namespace facebook::react;
|
|
22
25
|
|
|
@@ -33,8 +36,15 @@ static inline bool OptionsEqual(
|
|
|
33
36
|
}
|
|
34
37
|
} // namespace
|
|
35
38
|
|
|
39
|
+
@interface PCSelectionMenu ()
|
|
40
|
+
|
|
41
|
+
- (void)updateMeasurements;
|
|
42
|
+
|
|
43
|
+
@end
|
|
44
|
+
|
|
36
45
|
@implementation PCSelectionMenu {
|
|
37
46
|
PCSelectionMenuView *_view;
|
|
47
|
+
MeasuringPCSelectionMenuShadowNode::ConcreteState::Shared _state;
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
@@ -167,6 +177,38 @@ static inline bool OptionsEqual(
|
|
|
167
177
|
}
|
|
168
178
|
|
|
169
179
|
[super updateProps:props oldProps:oldProps];
|
|
180
|
+
|
|
181
|
+
// Update measurements when props change that affect layout
|
|
182
|
+
[self updateMeasurements];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
#pragma mark - State (Measuring)
|
|
186
|
+
|
|
187
|
+
- (void)updateState:(const State::Shared &)state
|
|
188
|
+
oldState:(const State::Shared &)oldState {
|
|
189
|
+
_state = std::static_pointer_cast<
|
|
190
|
+
const MeasuringPCSelectionMenuShadowNode::ConcreteState>(state);
|
|
191
|
+
|
|
192
|
+
if (oldState == nullptr) {
|
|
193
|
+
// First time: compute initial size.
|
|
194
|
+
[self updateMeasurements];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
[super updateState:state oldState:oldState];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
- (void)updateMeasurements {
|
|
201
|
+
if (_state == nullptr)
|
|
202
|
+
return;
|
|
203
|
+
|
|
204
|
+
// Use the real width Yoga gave us
|
|
205
|
+
const CGFloat w = self.bounds.size.width > 1 ? self.bounds.size.width : 320;
|
|
206
|
+
|
|
207
|
+
CGSize size = [_view sizeForLayoutWithConstrainedTo:CGSizeMake(w, 0)];
|
|
208
|
+
|
|
209
|
+
PCSelectionMenuStateFrameSize next;
|
|
210
|
+
next.frameSize = {(Float)size.width, (Float)size.height};
|
|
211
|
+
_state->updateState(std::move(next));
|
|
170
212
|
}
|
|
171
213
|
|
|
172
214
|
@end
|
|
@@ -370,6 +370,23 @@ public final class PCSelectionMenuView: UIControl {
|
|
|
370
370
|
)
|
|
371
371
|
return CGSize(width: UIView.noIntrinsicMetric, height: h)
|
|
372
372
|
}
|
|
373
|
+
|
|
374
|
+
/// Called by the measuring pipeline to get the size for Yoga layout.
|
|
375
|
+
/// Headless mode returns zero so Yoga reserves nothing.
|
|
376
|
+
@objc public func sizeForLayout(withConstrainedTo constrainedSize: CGSize) -> CGSize {
|
|
377
|
+
guard anchorMode == "inline" else { return .zero }
|
|
378
|
+
|
|
379
|
+
guard let host = hostingController else {
|
|
380
|
+
return CGSize(width: constrainedSize.width, height: PCConstants.minTouchTargetHeight)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
host.view.setNeedsLayout()
|
|
384
|
+
host.view.layoutIfNeeded()
|
|
385
|
+
|
|
386
|
+
let w = constrainedSize.width > 1 ? constrainedSize.width : PCConstants.fallbackWidth
|
|
387
|
+
let fitted = host.sizeThatFits(in: CGSize(width: w, height: .greatestFiniteMagnitude))
|
|
388
|
+
return CGSize(width: constrainedSize.width, height: max(PCConstants.minTouchTargetHeight, fitted.height))
|
|
389
|
+
}
|
|
373
390
|
}
|
|
374
391
|
|
|
375
392
|
// MARK: - Glass Menu Cell
|
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
// SelectionMenu.tsx
|
|
4
4
|
import React, { useCallback, useMemo } from 'react';
|
|
5
|
-
import { Platform, StyleSheet } from 'react-native';
|
|
6
5
|
import NativeSelectionMenu from './SelectionMenuNativeComponent';
|
|
7
6
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
7
|
function normalizeSelectedData(selected) {
|
|
9
8
|
return selected ?? '';
|
|
10
9
|
}
|
|
11
|
-
function normalizeNativeVisible(
|
|
12
|
-
//
|
|
13
|
-
if (
|
|
10
|
+
function normalizeNativeVisible(presentation, visible) {
|
|
11
|
+
// Embedded mode ignores visible; keep it undefined so native isn't spammed.
|
|
12
|
+
if (presentation === 'embedded') return undefined;
|
|
14
13
|
return visible ? 'open' : 'closed';
|
|
15
14
|
}
|
|
16
15
|
export function SelectionMenu(props) {
|
|
@@ -20,7 +19,7 @@ export function SelectionMenu(props) {
|
|
|
20
19
|
selected,
|
|
21
20
|
disabled,
|
|
22
21
|
placeholder,
|
|
23
|
-
|
|
22
|
+
presentation = 'modal',
|
|
24
23
|
visible,
|
|
25
24
|
onSelect,
|
|
26
25
|
onRequestClose,
|
|
@@ -29,7 +28,7 @@ export function SelectionMenu(props) {
|
|
|
29
28
|
...viewProps
|
|
30
29
|
} = props;
|
|
31
30
|
const selectedData = useMemo(() => normalizeSelectedData(selected), [selected]);
|
|
32
|
-
const nativeVisible = useMemo(() => normalizeNativeVisible(
|
|
31
|
+
const nativeVisible = useMemo(() => normalizeNativeVisible(presentation, visible), [presentation, visible]);
|
|
33
32
|
const handleSelect = useCallback(e => {
|
|
34
33
|
const {
|
|
35
34
|
index,
|
|
@@ -49,14 +48,13 @@ export function SelectionMenu(props) {
|
|
|
49
48
|
material: android.material
|
|
50
49
|
};
|
|
51
50
|
}, [android]);
|
|
52
|
-
const isAndroidM3Inline = android?.material && inlineMode && android.material === 'm3' && Platform.OS === 'android';
|
|
53
51
|
return /*#__PURE__*/_jsx(NativeSelectionMenu, {
|
|
54
|
-
style:
|
|
52
|
+
style: style,
|
|
55
53
|
options: options,
|
|
56
54
|
selectedData: selectedData,
|
|
57
55
|
interactivity: disabled ? 'disabled' : 'enabled',
|
|
58
56
|
placeholder: placeholder,
|
|
59
|
-
anchorMode:
|
|
57
|
+
anchorMode: presentation === 'embedded' ? 'inline' : 'headless',
|
|
60
58
|
visible: nativeVisible,
|
|
61
59
|
onSelect: onSelect ? handleSelect : undefined,
|
|
62
60
|
onRequestClose: onRequestClose ? handleRequestClose : undefined,
|
|
@@ -65,9 +63,4 @@ export function SelectionMenu(props) {
|
|
|
65
63
|
...viewProps
|
|
66
64
|
});
|
|
67
65
|
}
|
|
68
|
-
const styles = StyleSheet.create({
|
|
69
|
-
androidInline: {
|
|
70
|
-
minHeight: 60
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
66
|
//# sourceMappingURL=SelectionMenu.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","useCallback","useMemo","
|
|
1
|
+
{"version":3,"names":["React","useCallback","useMemo","NativeSelectionMenu","jsx","_jsx","normalizeSelectedData","selected","normalizeNativeVisible","presentation","visible","undefined","SelectionMenu","props","style","options","disabled","placeholder","onSelect","onRequestClose","ios","android","viewProps","selectedData","nativeVisible","handleSelect","e","index","label","data","nativeEvent","handleRequestClose","nativeAndroid","material","interactivity","anchorMode"],"sourceRoot":"../../src","sources":["SelectionMenu.tsx"],"mappings":";;AAAA;AACA,OAAOA,KAAK,IAAIC,WAAW,EAAEC,OAAO,QAAQ,OAAO;AAGnD,OAAOC,mBAAmB,MAGnB,gCAAgC;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAuDxC,SAASC,qBAAqBA,CAACC,QAAuB,EAAU;EAC9D,OAAOA,QAAQ,IAAI,EAAE;AACvB;AAEA,SAASC,sBAAsBA,CAC7BC,YAAsC,EACtCC,OAA4B,EACG;EAC/B;EACA,IAAID,YAAY,KAAK,UAAU,EAAE,OAAOE,SAAS;EACjD,OAAOD,OAAO,GAAG,MAAM,GAAG,QAAQ;AACpC;AAEA,OAAO,SAASE,aAAaA,CAACC,KAAyB,EAAsB;EAC3E,MAAM;IACJC,KAAK;IACLC,OAAO;IACPR,QAAQ;IACRS,QAAQ;IACRC,WAAW;IACXR,YAAY,GAAG,OAAO;IACtBC,OAAO;IACPQ,QAAQ;IACRC,cAAc;IACdC,GAAG;IACHC,OAAO;IACP,GAAGC;EACL,CAAC,GAAGT,KAAK;EAET,MAAMU,YAAY,GAAGrB,OAAO,CAC1B,MAAMI,qBAAqB,CAACC,QAAQ,CAAC,EACrC,CAACA,QAAQ,CACX,CAAC;EAED,MAAMiB,aAAa,GAAGtB,OAAO,CAC3B,MAAMM,sBAAsB,CAACC,YAAY,EAAEC,OAAO,CAAC,EACnD,CAACD,YAAY,EAAEC,OAAO,CACxB,CAAC;EAED,MAAMe,YAAY,GAAGxB,WAAW,CAC7ByB,CAA4C,IAAK;IAChD,MAAM;MAAEC,KAAK;MAAEC,KAAK;MAAEC;IAAK,CAAC,GAAGH,CAAC,CAACI,WAAW;IAC5CZ,QAAQ,GAAGW,IAAI,EAAED,KAAK,EAAED,KAAK,CAAC;EAChC,CAAC,EACD,CAACT,QAAQ,CACX,CAAC;EAED,MAAMa,kBAAkB,GAAG9B,WAAW,CAAC,MAAM;IAC3CkB,cAAc,GAAG,CAAC;EACpB,CAAC,EAAE,CAACA,cAAc,CAAC,CAAC;;EAEpB;EACA,MAAMa,aAAa,GAAG9B,OAAO,CAAC,MAAM;IAClC,IAAI,CAACmB,OAAO,EAAE,OAAOV,SAAS;IAC9B,OAAO;MAAEsB,QAAQ,EAAEZ,OAAO,CAACY;IAAS,CAAC;EACvC,CAAC,EAAE,CAACZ,OAAO,CAAC,CAAC;EAEb,oBACEhB,IAAA,CAACF,mBAAmB;IAClBW,KAAK,EAAEA,KAAM;IACbC,OAAO,EAAEA,OAAQ;IACjBQ,YAAY,EAAEA,YAAa;IAC3BW,aAAa,EAAElB,QAAQ,GAAG,UAAU,GAAG,SAAU;IACjDC,WAAW,EAAEA,WAAY;IACzBkB,UAAU,EAAE1B,YAAY,KAAK,UAAU,GAAG,QAAQ,GAAG,UAAW;IAChEC,OAAO,EAAEc,aAAc;IACvBN,QAAQ,EAAEA,QAAQ,GAAGO,YAAY,GAAGd,SAAU;IAC9CQ,cAAc,EAAEA,cAAc,GAAGY,kBAAkB,GAAGpB,SAAU;IAChES,GAAG,EAAEA,GAAI;IACTC,OAAO,EAAEW,aAAc;IAAA,GACnBV;EAAS,CACd,CAAC;AAEN","ignoreList":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type ViewProps } from 'react-native';
|
|
3
3
|
import { type SelectionMenuOption } from './SelectionMenuNativeComponent';
|
|
4
|
-
import type { AndroidMaterialMode } from './sharedTypes';
|
|
4
|
+
import type { AndroidMaterialMode, Presentation } from './sharedTypes';
|
|
5
5
|
export interface SelectionMenuProps extends ViewProps {
|
|
6
6
|
/** Options are label + data (payload) */
|
|
7
7
|
options: readonly SelectionMenuOption[];
|
|
@@ -13,12 +13,13 @@ export interface SelectionMenuProps extends ViewProps {
|
|
|
13
13
|
disabled?: boolean;
|
|
14
14
|
placeholder?: string;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* Presentation mode:
|
|
17
|
+
* - 'modal' (default): Headless mode, controlled by `visible` prop.
|
|
18
|
+
* - 'embedded': Native renders its own inline anchor and manages open/close internally.
|
|
18
19
|
*/
|
|
19
|
-
|
|
20
|
+
presentation?: Presentation;
|
|
20
21
|
/**
|
|
21
|
-
*
|
|
22
|
+
* Modal mode only (presentation === 'modal'):
|
|
22
23
|
* controls whether the native menu UI is presented.
|
|
23
24
|
*/
|
|
24
25
|
visible?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectionMenu.d.ts","sourceRoot":"","sources":["../../../src/SelectionMenu.tsx"],"names":[],"mappings":"AACA,OAAO,KAA+B,MAAM,OAAO,CAAC;AACpD,OAAO,
|
|
1
|
+
{"version":3,"file":"SelectionMenu.d.ts","sourceRoot":"","sources":["../../../src/SelectionMenu.tsx"],"names":[],"mappings":"AACA,OAAO,KAA+B,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAA4B,EAC1B,KAAK,mBAAmB,EAEzB,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEvE,MAAM,WAAW,kBAAmB,SAAQ,SAAS;IACnD,yCAAyC;IACzC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAExC;;;OAGG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAE5B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEhE;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAE5B;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,CAAC;IAET,OAAO,CAAC,EAAE;QACR,6CAA6C;QAC7C,QAAQ,CAAC,EAAE,mBAAmB,CAAC;KAChC,CAAC;IAEF,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAeD,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CA4D3E"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { CodegenTypes } from 'react-native';
|
|
2
|
-
/** Shared
|
|
2
|
+
/** Shared "open/closed" control state. */
|
|
3
3
|
export type Visible = 'open' | 'closed';
|
|
4
|
+
/** Shared presentation mode for pickers/menus. */
|
|
5
|
+
export type Presentation = 'modal' | 'embedded';
|
|
4
6
|
/** Shared Material preference (Android). */
|
|
5
7
|
export type AndroidMaterialMode = 'system' | 'm3';
|
|
6
8
|
/** Common event empty payload type. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sharedTypes.d.ts","sourceRoot":"","sources":["../../../src/sharedTypes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,0CAA0C;AAC1C,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAExC,4CAA4C;AAC5C,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,IAAI,CAAC;AAElD,uCAAuC;AACvC,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;AAEtC,oCAAoC;AACpC,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,YAAY,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"sharedTypes.d.ts","sourceRoot":"","sources":["../../../src/sharedTypes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,0CAA0C;AAC1C,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAExC,kDAAkD;AAClD,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,CAAC;AAEhD,4CAA4C;AAC5C,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,IAAI,CAAC;AAElD,uCAAuC;AACvC,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;AAEtC,oCAAoC;AACpC,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,YAAY,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-platform-components",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"description": "A cross-platform toolkit of native UI components for React Native.",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"release": "release-it",
|
|
43
43
|
"test": "jest",
|
|
44
44
|
"upgrade:check": "yarn dlx npm-check-updates",
|
|
45
|
-
"upgrade:apply": "yarn dlx npm-check-updates -u && rm -rf node_modules && rm -rf package-lock.json && yarn install"
|
|
45
|
+
"upgrade:apply": "yarn dlx npm-check-updates -u && rm -rf node_modules && rm -rf package-lock.json && yarn install",
|
|
46
|
+
"generate:gifs": "./scripts/generate-readme-gifs.sh"
|
|
46
47
|
},
|
|
47
48
|
"keywords": [
|
|
48
49
|
"react-native",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
dependency: {
|
|
3
|
+
platforms: {
|
|
4
|
+
android: {
|
|
5
|
+
componentDescriptors: [
|
|
6
|
+
'MeasuringPCSelectionMenuComponentDescriptor',
|
|
7
|
+
'MeasuringPCDatePickerComponentDescriptor',
|
|
8
|
+
],
|
|
9
|
+
cmakeListsPath: 'src/main/jni/CMakeLists.txt',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -1,52 +1,23 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
-
#include "PCDatePickerShadowNode-custom.h"
|
|
4
|
-
|
|
5
3
|
#include <react/renderer/core/ConcreteComponentDescriptor.h>
|
|
6
|
-
#include <react/renderer/mounting/ShadowView.h>
|
|
7
|
-
#include <react/renderer/core/ShadowNode.h>
|
|
8
|
-
#include <react/renderer/core/ShadowNodeTraits.h>
|
|
9
|
-
#include <react/renderer/core/ShadowNodeFragment.h>
|
|
10
|
-
#include <react/renderer/core/ShadowNodeFamily.h>
|
|
11
|
-
#include <react/renderer/components/view/YogaLayoutableShadowNode.h>
|
|
12
|
-
|
|
13
|
-
namespace facebook::react
|
|
14
|
-
{
|
|
15
4
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
|
|
5
|
+
// Forward declaration to avoid circular includes
|
|
6
|
+
namespace facebook::react {
|
|
7
|
+
class MeasuringPCDatePickerShadowNode;
|
|
8
|
+
}
|
|
21
9
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
auto &pickerShadowNode =
|
|
25
|
-
static_cast<MeasuringPCDatePickerShadowNode &>(shadowNode);
|
|
26
|
-
auto &layoutableShadowNode =
|
|
27
|
-
static_cast<YogaLayoutableShadowNode &>(pickerShadowNode);
|
|
28
|
-
|
|
29
|
-
auto state =
|
|
30
|
-
std::static_pointer_cast<
|
|
31
|
-
const MeasuringPCDatePickerShadowNode::ConcreteState
|
|
32
|
-
>(shadowNode.getState());
|
|
33
|
-
|
|
34
|
-
if (state)
|
|
35
|
-
{
|
|
36
|
-
auto stateData = state->getData();
|
|
37
|
-
auto frameSize = stateData.frameSize;
|
|
10
|
+
// Include the actual shadow node definition
|
|
11
|
+
#include "PCDatePickerShadowNode-custom.h"
|
|
38
12
|
|
|
39
|
-
|
|
40
|
-
{
|
|
41
|
-
layoutableShadowNode.setSize(Size{
|
|
42
|
-
frameSize.width,
|
|
43
|
-
frameSize.height,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
}
|
|
13
|
+
namespace facebook::react {
|
|
47
14
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Custom component descriptor that uses our measuring shadow node
|
|
17
|
+
* instead of the generated one. No custom adopt() needed since
|
|
18
|
+
* measurement is handled via measureContent().
|
|
19
|
+
*/
|
|
20
|
+
using MeasuringPCDatePickerComponentDescriptor =
|
|
21
|
+
ConcreteComponentDescriptor<MeasuringPCDatePickerShadowNode>;
|
|
51
22
|
|
|
52
23
|
} // namespace facebook::react
|
|
@@ -1 +1,36 @@
|
|
|
1
1
|
#include "PCDatePickerShadowNode-custom.h"
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/core/LayoutConstraints.h>
|
|
4
|
+
#include <react/renderer/core/ConcreteShadowNode.h>
|
|
5
|
+
#include <algorithm>
|
|
6
|
+
|
|
7
|
+
namespace facebook::react {
|
|
8
|
+
|
|
9
|
+
Size MeasuringPCDatePickerShadowNode::measureContent(
|
|
10
|
+
const LayoutContext& /*layoutContext*/,
|
|
11
|
+
const LayoutConstraints& layoutConstraints) const {
|
|
12
|
+
|
|
13
|
+
// Get frame size from native state - native measures the actual picker
|
|
14
|
+
const auto& stateData = this->getStateData();
|
|
15
|
+
Float measuredW = stateData.frameSize.width;
|
|
16
|
+
Float measuredH = stateData.frameSize.height;
|
|
17
|
+
|
|
18
|
+
// If width is 0, use available width from constraints
|
|
19
|
+
const Float kHuge = static_cast<Float>(1.0e9);
|
|
20
|
+
if (measuredW <= 0) {
|
|
21
|
+
const Float maxW = layoutConstraints.maximumSize.width;
|
|
22
|
+
measuredW = (maxW > 0 && maxW < kHuge) ? maxW : 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Respect layout constraints
|
|
26
|
+
measuredW = std::max<Float>(measuredW, layoutConstraints.minimumSize.width);
|
|
27
|
+
measuredW = std::min<Float>(measuredW, layoutConstraints.maximumSize.width);
|
|
28
|
+
|
|
29
|
+
measuredH = std::max<Float>(measuredH, layoutConstraints.minimumSize.height);
|
|
30
|
+
measuredH = std::min<Float>(measuredH, layoutConstraints.maximumSize.height);
|
|
31
|
+
|
|
32
|
+
Size result{measuredW, measuredH};
|
|
33
|
+
return layoutConstraints.clamp(result);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
} // namespace facebook::react
|
|
@@ -1,27 +1,49 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
-
#include <react/renderer/components/
|
|
3
|
+
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
4
|
+
|
|
5
|
+
// Only include what we need for the shadow node definition
|
|
6
|
+
// Do NOT include ComponentDescriptors.h here to avoid circular dependency
|
|
4
7
|
#include <react/renderer/components/PlatformComponentsViewSpec/EventEmitters.h>
|
|
5
8
|
#include <react/renderer/components/PlatformComponentsViewSpec/Props.h>
|
|
6
9
|
|
|
7
|
-
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
8
|
-
#include <react/renderer/core/ConcreteState.h>
|
|
9
|
-
#include <react/renderer/core/ShadowNodeTraits.h>
|
|
10
|
-
#include <react/renderer/mounting/ShadowView.h>
|
|
11
|
-
|
|
12
10
|
#include "PCDatePickerState-custom.h"
|
|
13
11
|
|
|
14
|
-
namespace facebook::react
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
12
|
+
namespace facebook::react {
|
|
13
|
+
|
|
14
|
+
extern const char PCDatePickerComponentName[];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Custom ShadowNode for DatePicker that supports Yoga measurement.
|
|
18
|
+
*
|
|
19
|
+
* Key behavior:
|
|
20
|
+
* - Native side measures the actual picker and updates state with frameSize
|
|
21
|
+
* - measureContent() returns the size from state for proper Yoga layout
|
|
22
|
+
* - No hardcoded dimensions - uses actual measured values from native
|
|
23
|
+
*/
|
|
24
|
+
class MeasuringPCDatePickerShadowNode final
|
|
25
|
+
: public ConcreteViewShadowNode<
|
|
26
|
+
PCDatePickerComponentName,
|
|
27
|
+
PCDatePickerProps,
|
|
28
|
+
PCDatePickerEventEmitter,
|
|
29
|
+
PCDatePickerStateFrameSize> {
|
|
30
|
+
public:
|
|
31
|
+
using ConcreteViewShadowNode::ConcreteViewShadowNode;
|
|
32
|
+
|
|
33
|
+
static ShadowNodeTraits BaseTraits() {
|
|
34
|
+
auto traits = ConcreteViewShadowNode::BaseTraits();
|
|
35
|
+
traits.set(ShadowNodeTraits::Trait::LeafYogaNode);
|
|
36
|
+
traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode);
|
|
37
|
+
return traits;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Called by Yoga when it needs the intrinsic size of the component.
|
|
42
|
+
* Returns the size provided by native through state - no hardcoding.
|
|
43
|
+
*/
|
|
44
|
+
Size measureContent(
|
|
45
|
+
const LayoutContext& layoutContext,
|
|
46
|
+
const LayoutConstraints& layoutConstraints) const override;
|
|
47
|
+
};
|
|
26
48
|
|
|
27
49
|
} // namespace facebook::react
|
|
@@ -1,13 +1,65 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <react/renderer/core/LayoutPrimitives.h>
|
|
4
|
+
#include <memory>
|
|
5
|
+
|
|
6
|
+
#ifdef RN_SERIALIZABLE_STATE
|
|
7
|
+
#include <folly/dynamic.h>
|
|
8
|
+
#include <react/renderer/mapbuffer/MapBuffer.h>
|
|
9
|
+
#include <react/renderer/mapbuffer/MapBufferBuilder.h>
|
|
10
|
+
#endif
|
|
4
11
|
|
|
5
12
|
namespace facebook::react {
|
|
6
13
|
|
|
7
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Custom state for DatePicker that holds the measured frame size from native.
|
|
16
|
+
* This allows the native side to measure the actual picker and communicate
|
|
17
|
+
* the size to the shadow node for proper Yoga layout.
|
|
18
|
+
*
|
|
19
|
+
* Note: Does NOT inherit from StateData (which is final). Custom state types
|
|
20
|
+
* are standalone structs that satisfy the ConcreteState template requirements.
|
|
21
|
+
*/
|
|
22
|
+
struct PCDatePickerStateFrameSize {
|
|
8
23
|
using Shared = std::shared_ptr<const PCDatePickerStateFrameSize>;
|
|
9
24
|
|
|
10
25
|
Size frameSize{}; // {width, height} in points
|
|
26
|
+
|
|
27
|
+
PCDatePickerStateFrameSize() = default;
|
|
28
|
+
|
|
29
|
+
explicit PCDatePickerStateFrameSize(Size size) : frameSize(size) {}
|
|
30
|
+
|
|
31
|
+
bool operator==(const PCDatePickerStateFrameSize& other) const {
|
|
32
|
+
return frameSize.width == other.frameSize.width &&
|
|
33
|
+
frameSize.height == other.frameSize.height;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
bool operator!=(const PCDatePickerStateFrameSize& other) const {
|
|
37
|
+
return !(*this == other);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#ifdef RN_SERIALIZABLE_STATE
|
|
41
|
+
// Required for Android state serialization
|
|
42
|
+
PCDatePickerStateFrameSize(
|
|
43
|
+
const PCDatePickerStateFrameSize& previousState,
|
|
44
|
+
folly::dynamic data)
|
|
45
|
+
: frameSize(previousState.frameSize) {
|
|
46
|
+
// Parse frame size from dynamic data if provided
|
|
47
|
+
if (data.isObject()) {
|
|
48
|
+
if (data.count("width") && data.count("height")) {
|
|
49
|
+
frameSize.width = static_cast<Float>(data["width"].asDouble());
|
|
50
|
+
frameSize.height = static_cast<Float>(data["height"].asDouble());
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
folly::dynamic getDynamic() const {
|
|
56
|
+
return folly::dynamic::object("width", frameSize.width)("height", frameSize.height);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
MapBuffer getMapBuffer() const {
|
|
60
|
+
return MapBufferBuilder::EMPTY();
|
|
61
|
+
}
|
|
62
|
+
#endif
|
|
11
63
|
};
|
|
12
64
|
|
|
13
65
|
} // namespace facebook::react
|
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
-
#include "PCSelectionMenuShadowNode-custom.h"
|
|
4
|
-
|
|
5
3
|
#include <react/renderer/core/ConcreteComponentDescriptor.h>
|
|
6
|
-
#include <react/renderer/mounting/ShadowView.h>
|
|
7
|
-
#include <react/renderer/core/ShadowNode.h>
|
|
8
|
-
#include <react/renderer/core/ShadowNodeTraits.h>
|
|
9
|
-
#include <react/renderer/core/ShadowNodeFragment.h>
|
|
10
|
-
#include <react/renderer/core/ShadowNodeFamily.h>
|
|
11
|
-
#include <react/renderer/components/view/YogaLayoutableShadowNode.h>
|
|
12
4
|
|
|
13
|
-
|
|
14
|
-
{
|
|
15
|
-
|
|
5
|
+
// Forward declaration to avoid circular includes
|
|
6
|
+
namespace facebook::react {
|
|
7
|
+
class MeasuringPCSelectionMenuShadowNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Include the actual shadow node definition
|
|
11
|
+
#include "PCSelectionMenuShadowNode-custom.h"
|
|
12
|
+
|
|
13
|
+
namespace facebook::react {
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
};*/
|
|
15
|
+
/**
|
|
16
|
+
* Custom component descriptor that uses our measuring shadow node
|
|
17
|
+
* instead of the generated one.
|
|
18
|
+
*/
|
|
19
|
+
using MeasuringPCSelectionMenuComponentDescriptor =
|
|
20
|
+
ConcreteComponentDescriptor<MeasuringPCSelectionMenuShadowNode>;
|
|
24
21
|
|
|
25
22
|
} // namespace facebook::react
|
|
@@ -1,36 +1,57 @@
|
|
|
1
|
-
#include <react/renderer/core/LayoutConstraints.h>
|
|
2
1
|
#include "PCSelectionMenuShadowNode-custom.h"
|
|
3
2
|
|
|
3
|
+
#include <react/renderer/core/LayoutConstraints.h>
|
|
4
4
|
#include <algorithm>
|
|
5
5
|
|
|
6
6
|
namespace facebook::react {
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const Float kHuge = static_cast<Float>(1.0e9);
|
|
8
|
+
Size MeasuringPCSelectionMenuShadowNode::measureContent(
|
|
9
|
+
const LayoutContext& /*layoutContext*/,
|
|
10
|
+
const LayoutConstraints& layoutConstraints) const {
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const auto& props = *std::static_pointer_cast<const PCSelectionMenuProps>(getProps());
|
|
18
|
-
const bool inlineMode = props.anchorMode == "inline";
|
|
12
|
+
const auto& props = *std::static_pointer_cast<const PCSelectionMenuProps>(getProps());
|
|
13
|
+
const bool inlineMode = props.anchorMode == "inline";
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
// Headless mode: zero size
|
|
16
|
+
if (!inlineMode) {
|
|
17
|
+
return layoutConstraints.clamp(Size{0, 0});
|
|
18
|
+
}
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
// Get frame size from native state - native measures the actual picker
|
|
21
|
+
const auto& stateData = this->getStateData();
|
|
22
|
+
Float measuredW = stateData.frameSize.width;
|
|
23
|
+
Float measuredH = stateData.frameSize.height;
|
|
24
|
+
|
|
25
|
+
// If height is 0, use fallback values (state not yet set by native)
|
|
26
|
+
if (measuredH <= 0) {
|
|
27
|
+
#ifdef __ANDROID__
|
|
28
|
+
const std::string& material = props.android.material;
|
|
29
|
+
if (material == "m3") {
|
|
30
|
+
measuredH = static_cast<Float>(kFallbackHeightAndroidM3);
|
|
24
31
|
} else {
|
|
25
|
-
measuredH =
|
|
32
|
+
measuredH = static_cast<Float>(kFallbackHeightAndroid);
|
|
26
33
|
}
|
|
34
|
+
#else
|
|
35
|
+
measuredH = static_cast<Float>(kFallbackHeightIOS);
|
|
36
|
+
#endif
|
|
37
|
+
}
|
|
27
38
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return layoutConstraints.clamp(result);
|
|
39
|
+
// If width is 0, use available width from constraints
|
|
40
|
+
const Float kHuge = static_cast<Float>(1.0e9);
|
|
41
|
+
if (measuredW <= 0) {
|
|
42
|
+
const Float maxW = layoutConstraints.maximumSize.width;
|
|
43
|
+
measuredW = (maxW > 0 && maxW < kHuge) ? maxW : 0;
|
|
34
44
|
}
|
|
35
45
|
|
|
46
|
+
// Respect layout constraints
|
|
47
|
+
measuredW = std::max<Float>(measuredW, layoutConstraints.minimumSize.width);
|
|
48
|
+
measuredW = std::min<Float>(measuredW, layoutConstraints.maximumSize.width);
|
|
49
|
+
|
|
50
|
+
measuredH = std::max<Float>(measuredH, layoutConstraints.minimumSize.height);
|
|
51
|
+
measuredH = std::min<Float>(measuredH, layoutConstraints.maximumSize.height);
|
|
52
|
+
|
|
53
|
+
Size result{measuredW, measuredH};
|
|
54
|
+
return layoutConstraints.clamp(result);
|
|
55
|
+
}
|
|
56
|
+
|
|
36
57
|
} // namespace facebook::react
|