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.
Files changed (32) hide show
  1. package/README.md +13 -8
  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 +58 -2
  11. package/ios/PCSelectionMenu.mm +42 -0
  12. package/ios/PCSelectionMenu.swift +17 -0
  13. package/lib/module/SelectionMenu.js +7 -14
  14. package/lib/module/SelectionMenu.js.map +1 -1
  15. package/lib/typescript/src/SelectionMenu.d.ts +6 -5
  16. package/lib/typescript/src/SelectionMenu.d.ts.map +1 -1
  17. package/lib/typescript/src/sharedTypes.d.ts +3 -1
  18. package/lib/typescript/src/sharedTypes.d.ts.map +1 -1
  19. package/package.json +3 -2
  20. package/react-native.config.js +13 -0
  21. package/shared/PCDatePickerComponentDescriptors-custom.h +14 -43
  22. package/shared/PCDatePickerShadowNode-custom.cpp +35 -0
  23. package/shared/PCDatePickerShadowNode-custom.h +40 -18
  24. package/shared/PCDatePickerState-custom.h +53 -1
  25. package/shared/PCSelectionMenuComponentDescriptors-custom.h +15 -18
  26. package/shared/PCSelectionMenuShadowNode-custom.cpp +42 -21
  27. package/shared/PCSelectionMenuShadowNode-custom.h +23 -10
  28. package/shared/PCSelectionMenuState-custom.h +65 -0
  29. package/shared/README.md +179 -0
  30. package/shared/react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h +9 -0
  31. package/src/SelectionMenu.tsx +15 -24
  32. package/src/sharedTypes.ts +4 -1
@@ -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(inlineMode, visible) {
12
- // Inline mode ignores visible; keep it undefined so native isn't spammed.
13
- if (inlineMode) return undefined;
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
- inlineMode,
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(inlineMode, visible), [inlineMode, visible]);
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: [style, isAndroidM3Inline && styles.androidInline],
52
+ style: style,
55
53
  options: options,
56
54
  selectedData: selectedData,
57
55
  interactivity: disabled ? 'disabled' : 'enabled',
58
56
  placeholder: placeholder,
59
- anchorMode: inlineMode ? 'inline' : 'headless',
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","Platform","StyleSheet","NativeSelectionMenu","jsx","_jsx","normalizeSelectedData","selected","normalizeNativeVisible","inlineMode","visible","undefined","SelectionMenu","props","style","options","disabled","placeholder","onSelect","onRequestClose","ios","android","viewProps","selectedData","nativeVisible","handleSelect","e","index","label","data","nativeEvent","handleRequestClose","nativeAndroid","material","isAndroidM3Inline","OS","styles","androidInline","interactivity","anchorMode","create","minHeight"],"sourceRoot":"../../src","sources":["SelectionMenu.tsx"],"mappings":";;AAAA;AACA,OAAOA,KAAK,IAAIC,WAAW,EAAEC,OAAO,QAAQ,OAAO;AACnD,SAASC,QAAQ,EAAEC,UAAU,QAAwB,cAAc;AAEnE,OAAOC,mBAAmB,MAGnB,gCAAgC;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAsDxC,SAASC,qBAAqBA,CAACC,QAAuB,EAAU;EAC9D,OAAOA,QAAQ,IAAI,EAAE;AACvB;AAEA,SAASC,sBAAsBA,CAC7BC,UAA+B,EAC/BC,OAA4B,EACG;EAC/B;EACA,IAAID,UAAU,EAAE,OAAOE,SAAS;EAChC,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,UAAU;IACVC,OAAO;IACPQ,QAAQ;IACRC,cAAc;IACdC,GAAG;IACHC,OAAO;IACP,GAAGC;EACL,CAAC,GAAGT,KAAK;EAET,MAAMU,YAAY,GAAGvB,OAAO,CAC1B,MAAMM,qBAAqB,CAACC,QAAQ,CAAC,EACrC,CAACA,QAAQ,CACX,CAAC;EAED,MAAMiB,aAAa,GAAGxB,OAAO,CAC3B,MAAMQ,sBAAsB,CAACC,UAAU,EAAEC,OAAO,CAAC,EACjD,CAACD,UAAU,EAAEC,OAAO,CACtB,CAAC;EAED,MAAMe,YAAY,GAAG1B,WAAW,CAC7B2B,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,GAAGhC,WAAW,CAAC,MAAM;IAC3CoB,cAAc,GAAG,CAAC;EACpB,CAAC,EAAE,CAACA,cAAc,CAAC,CAAC;;EAEpB;EACA,MAAMa,aAAa,GAAGhC,OAAO,CAAC,MAAM;IAClC,IAAI,CAACqB,OAAO,EAAE,OAAOV,SAAS;IAC9B,OAAO;MAAEsB,QAAQ,EAAEZ,OAAO,CAACY;IAAS,CAAC;EACvC,CAAC,EAAE,CAACZ,OAAO,CAAC,CAAC;EAEb,MAAMa,iBAAiB,GACrBb,OAAO,EAAEY,QAAQ,IACjBxB,UAAU,IACVY,OAAO,CAACY,QAAQ,KAAK,IAAI,IACzBhC,QAAQ,CAACkC,EAAE,KAAK,SAAS;EAE3B,oBACE9B,IAAA,CAACF,mBAAmB;IAClBW,KAAK,EAAE,CAACA,KAAK,EAAEoB,iBAAiB,IAAIE,MAAM,CAACC,aAAa,CAAE;IAC1DtB,OAAO,EAAEA,OAAQ;IACjBQ,YAAY,EAAEA,YAAa;IAC3Be,aAAa,EAAEtB,QAAQ,GAAG,UAAU,GAAG,SAAU;IACjDC,WAAW,EAAEA,WAAY;IACzBsB,UAAU,EAAE9B,UAAU,GAAG,QAAQ,GAAG,UAAW;IAC/CC,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;AAEA,MAAMc,MAAM,GAAGlC,UAAU,CAACsC,MAAM,CAAC;EAC/BH,aAAa,EAAE;IAAEI,SAAS,EAAE;EAAG;AACjC,CAAC,CAAC","ignoreList":[]}
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
- * If true, native renders its own inline anchor and manages open/close internally.
17
- * If false (default), component is headless and controlled by `visible`.
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
- inlineMode?: boolean;
20
+ presentation?: Presentation;
20
21
  /**
21
- * Headless mode only (inlineMode === false):
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,EAAwB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,OAA4B,EAC1B,KAAK,mBAAmB,EAEzB,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEzD,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;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;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,CAkE3E"}
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 open/closed control state. */
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",
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
- class MeasuringPCDatePickerComponentDescriptor final
17
- : public ConcreteComponentDescriptor<MeasuringPCDatePickerShadowNode>
18
- {
19
- public:
20
- using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
5
+ // Forward declaration to avoid circular includes
6
+ namespace facebook::react {
7
+ class MeasuringPCDatePickerShadowNode;
8
+ }
21
9
 
22
- void adopt(ShadowNode &shadowNode) const override
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
- if (frameSize.width >= 0 && frameSize.height >= 0)
40
- {
41
- layoutableShadowNode.setSize(Size{
42
- frameSize.width,
43
- frameSize.height,
44
- });
45
- }
46
- }
13
+ namespace facebook::react {
47
14
 
48
- ConcreteComponentDescriptor::adopt(shadowNode);
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/PlatformComponentsViewSpec/ComponentDescriptors.h>
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
- class MeasuringPCDatePickerShadowNode final
17
- : public ConcreteViewShadowNode<
18
- PCDatePickerComponentName,
19
- PCDatePickerProps,
20
- PCDatePickerEventEmitter,
21
- PCDatePickerStateFrameSize>
22
- {
23
- public:
24
- using ConcreteViewShadowNode::ConcreteViewShadowNode;
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
- struct PCDatePickerStateFrameSize final {
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
- namespace facebook::react
14
- {
15
- using MeasuringPCSelectionMenuComponentDescriptor = ConcreteComponentDescriptor<MeasuringPCSelectionMenuShadowNode>;
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
- class MeasuringPCDatePickerComponentDescriptor final
19
- : public ConcreteComponentDescriptor<MeasuringPCSelectionMenuShadowNode>
20
- {
21
- public:
22
- using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
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
- Size MeasuringPCSelectionMenuShadowNode::measureContent(
9
- const LayoutContext&,
10
- const LayoutConstraints& layoutConstraints) const {
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
- const Float maxW = layoutConstraints.maximumSize.width;
15
- const Float measuredW = (maxW > 0 && maxW < kHuge) ? maxW : 0;
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
- Float measuredH = 0;
15
+ // Headless mode: zero size
16
+ if (!inlineMode) {
17
+ return layoutConstraints.clamp(Size{0, 0});
18
+ }
21
19
 
22
- if (inlineMode) {
23
- measuredH = static_cast<Float>(kMinRowHeight); // 44
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 = 0;
32
+ measuredH = static_cast<Float>(kFallbackHeightAndroid);
26
33
  }
34
+ #else
35
+ measuredH = static_cast<Float>(kFallbackHeightIOS);
36
+ #endif
37
+ }
27
38
 
28
- // Respect layout constraints (min/max) coming from JS styles
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);
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