react-native-platform-components 0.5.4 → 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 CHANGED
@@ -125,7 +125,7 @@ export function Example() {
125
125
  <SelectionMenu
126
126
  options={options}
127
127
  selected={value}
128
- inlineMode
128
+ presentation="embedded"
129
129
  placeholder="Select fruit"
130
130
  onSelect={(data) => setValue(data)}
131
131
  android={{ material: 'm3' }}
@@ -194,7 +194,7 @@ export function Example() {
194
194
 
195
195
  ## SelectionMenu
196
196
 
197
- Native selection menu with **inline** and **headless** modes.
197
+ Native selection menu with **modal** and **embedded** modes.
198
198
 
199
199
  ### Props
200
200
 
@@ -204,18 +204,18 @@ Native selection menu with **inline** and **headless** modes.
204
204
  | `selected` | `string \| null` | Currently selected option's `data` value |
205
205
  | `disabled` | `boolean` | Disables the menu |
206
206
  | `placeholder` | `string` | Placeholder text when no selection |
207
- | `inlineMode` | `boolean` | If true, renders native inline picker UI |
208
- | `visible` | `boolean` | Controls headless mode menu visibility |
207
+ | `presentation` | `'modal' \| 'embedded'` | Presentation mode (default: `'modal'`) |
208
+ | `visible` | `boolean` | Controls modal mode menu visibility |
209
209
  | `onSelect` | `(data, label, index) => void` | Called when user selects an option |
210
210
  | `onRequestClose` | `() => void` | Called when menu is dismissed without selection |
211
211
  | `android.material` | `'system' \| 'm3'` | Material Design style preference |
212
212
 
213
213
  ### Modes
214
214
 
215
- - **Headless mode** (default): Menu visibility controlled by `visible` prop. Use for custom trigger UI.
216
- - **Inline mode** (`inlineMode={true}`): Native picker UI rendered inline. Menu managed internally.
215
+ - **Modal mode** (default): Menu visibility controlled by `visible` prop. Use for custom trigger UI.
216
+ - **Embedded mode** (`presentation="embedded"`): Native picker UI rendered inline. Menu managed internally.
217
217
 
218
- > **Note:** On iOS, headless mode uses a custom popover to enable programmatic presentation. For the full native menu experience (system animations, scroll physics), use inline mode. This is an intentional trade-off: headless gives you control over the trigger UI, inline gives you the complete system menu behavior.
218
+ > **Note:** On iOS, modal mode uses a custom popover to enable programmatic presentation. For the full native menu experience (system animations, scroll physics), use embedded mode. This is an intentional trade-off: modal gives you control over the trigger UI, embedded gives you the complete system menu behavior.
219
219
 
220
220
  ---
221
221
 
@@ -120,6 +120,41 @@ public final class PCDatePickerView: UIControl,
120
120
 
121
121
  // MARK: - Layout / Sizing
122
122
 
123
+ private var lastLayoutBounds: CGRect = .zero
124
+ private var needsStyleReset = false
125
+
126
+ public override func layoutSubviews() {
127
+ super.layoutSubviews()
128
+
129
+ // For embedded presentation, manually center the picker after Auto Layout
130
+ // This ensures consistent centering regardless of UIDatePicker's internal state
131
+ if presentation == "embedded" && picker.superview === self && bounds.width > 0 {
132
+ let widthChanged = abs(bounds.width - lastLayoutBounds.width) > 1
133
+ if needsStyleReset || widthChanged {
134
+ if #available(iOS 13.4, *) {
135
+ let currentStyle = picker.preferredDatePickerStyle
136
+ picker.preferredDatePickerStyle = .automatic
137
+ picker.preferredDatePickerStyle = currentStyle
138
+ }
139
+ picker.sizeToFit()
140
+ needsStyleReset = false
141
+ }
142
+ lastLayoutBounds = bounds
143
+
144
+ // After constraints do their thing, manually adjust picker position to center it
145
+ let pickerSize = picker.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
146
+ let xOffset = (bounds.width - pickerSize.width) / 2
147
+ if xOffset > 0 {
148
+ picker.frame = CGRect(
149
+ x: xOffset,
150
+ y: 0,
151
+ width: pickerSize.width,
152
+ height: bounds.height
153
+ )
154
+ }
155
+ }
156
+ }
157
+
123
158
  private func invalidateSize() {
124
159
  invalidateIntrinsicContentSize()
125
160
  setNeedsLayout()
@@ -192,6 +227,10 @@ public final class PCDatePickerView: UIControl,
192
227
  picker.removeFromSuperview()
193
228
  addSubview(picker)
194
229
 
230
+ // Mark that we need a style reset on next layout pass
231
+ // This ensures centering is recalculated after Yoga provides correct bounds
232
+ needsStyleReset = true
233
+
195
234
  inlineConstraints = [
196
235
  picker.topAnchor.constraint(equalTo: topAnchor),
197
236
  picker.bottomAnchor.constraint(equalTo: bottomAnchor),
@@ -7,9 +7,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
7
7
  function normalizeSelectedData(selected) {
8
8
  return selected ?? '';
9
9
  }
10
- function normalizeNativeVisible(inlineMode, visible) {
11
- // Inline mode ignores visible; keep it undefined so native isn't spammed.
12
- 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;
13
13
  return visible ? 'open' : 'closed';
14
14
  }
15
15
  export function SelectionMenu(props) {
@@ -19,7 +19,7 @@ export function SelectionMenu(props) {
19
19
  selected,
20
20
  disabled,
21
21
  placeholder,
22
- inlineMode,
22
+ presentation = 'modal',
23
23
  visible,
24
24
  onSelect,
25
25
  onRequestClose,
@@ -28,7 +28,7 @@ export function SelectionMenu(props) {
28
28
  ...viewProps
29
29
  } = props;
30
30
  const selectedData = useMemo(() => normalizeSelectedData(selected), [selected]);
31
- const nativeVisible = useMemo(() => normalizeNativeVisible(inlineMode, visible), [inlineMode, visible]);
31
+ const nativeVisible = useMemo(() => normalizeNativeVisible(presentation, visible), [presentation, visible]);
32
32
  const handleSelect = useCallback(e => {
33
33
  const {
34
34
  index,
@@ -54,7 +54,7 @@ export function SelectionMenu(props) {
54
54
  selectedData: selectedData,
55
55
  interactivity: disabled ? 'disabled' : 'enabled',
56
56
  placeholder: placeholder,
57
- anchorMode: inlineMode ? 'inline' : 'headless',
57
+ anchorMode: presentation === 'embedded' ? 'inline' : 'headless',
58
58
  visible: nativeVisible,
59
59
  onSelect: onSelect ? handleSelect : undefined,
60
60
  onRequestClose: onRequestClose ? handleRequestClose : undefined,
@@ -1 +1 @@
1
- {"version":3,"names":["React","useCallback","useMemo","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","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;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,GAAGrB,OAAO,CAC1B,MAAMI,qBAAqB,CAACC,QAAQ,CAAC,EACrC,CAACA,QAAQ,CACX,CAAC;EAED,MAAMiB,aAAa,GAAGtB,OAAO,CAC3B,MAAMM,sBAAsB,CAACC,UAAU,EAAEC,OAAO,CAAC,EACjD,CAACD,UAAU,EAAEC,OAAO,CACtB,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,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","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,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,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,CA4D3E"}
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.4",
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",
@@ -7,7 +7,7 @@ import NativeSelectionMenu, {
7
7
  type SelectionMenuSelectEvent,
8
8
  } from './SelectionMenuNativeComponent';
9
9
 
10
- import type { AndroidMaterialMode } from './sharedTypes';
10
+ import type { AndroidMaterialMode, Presentation } from './sharedTypes';
11
11
 
12
12
  export interface SelectionMenuProps extends ViewProps {
13
13
  /** Options are label + data (payload) */
@@ -23,13 +23,14 @@ export interface SelectionMenuProps extends ViewProps {
23
23
  placeholder?: string;
24
24
 
25
25
  /**
26
- * If true, native renders its own inline anchor and manages open/close internally.
27
- * If false (default), component is headless and controlled by `visible`.
26
+ * Presentation mode:
27
+ * - 'modal' (default): Headless mode, controlled by `visible` prop.
28
+ * - 'embedded': Native renders its own inline anchor and manages open/close internally.
28
29
  */
29
- inlineMode?: boolean;
30
+ presentation?: Presentation;
30
31
 
31
32
  /**
32
- * Headless mode only (inlineMode === false):
33
+ * Modal mode only (presentation === 'modal'):
33
34
  * controls whether the native menu UI is presented.
34
35
  */
35
36
  visible?: boolean;
@@ -64,11 +65,11 @@ function normalizeSelectedData(selected: string | null): string {
64
65
  }
65
66
 
66
67
  function normalizeNativeVisible(
67
- inlineMode: boolean | undefined,
68
+ presentation: Presentation | undefined,
68
69
  visible: boolean | undefined
69
70
  ): 'open' | 'closed' | undefined {
70
- // Inline mode ignores visible; keep it undefined so native isn't spammed.
71
- if (inlineMode) return undefined;
71
+ // Embedded mode ignores visible; keep it undefined so native isn't spammed.
72
+ if (presentation === 'embedded') return undefined;
72
73
  return visible ? 'open' : 'closed';
73
74
  }
74
75
 
@@ -79,7 +80,7 @@ export function SelectionMenu(props: SelectionMenuProps): React.ReactElement {
79
80
  selected,
80
81
  disabled,
81
82
  placeholder,
82
- inlineMode,
83
+ presentation = 'modal',
83
84
  visible,
84
85
  onSelect,
85
86
  onRequestClose,
@@ -94,8 +95,8 @@ export function SelectionMenu(props: SelectionMenuProps): React.ReactElement {
94
95
  );
95
96
 
96
97
  const nativeVisible = useMemo(
97
- () => normalizeNativeVisible(inlineMode, visible),
98
- [inlineMode, visible]
98
+ () => normalizeNativeVisible(presentation, visible),
99
+ [presentation, visible]
99
100
  );
100
101
 
101
102
  const handleSelect = useCallback(
@@ -123,7 +124,7 @@ export function SelectionMenu(props: SelectionMenuProps): React.ReactElement {
123
124
  selectedData={selectedData}
124
125
  interactivity={disabled ? 'disabled' : 'enabled'}
125
126
  placeholder={placeholder}
126
- anchorMode={inlineMode ? 'inline' : 'headless'}
127
+ anchorMode={presentation === 'embedded' ? 'inline' : 'headless'}
127
128
  visible={nativeVisible}
128
129
  onSelect={onSelect ? handleSelect : undefined}
129
130
  onRequestClose={onRequestClose ? handleRequestClose : undefined}
@@ -1,9 +1,12 @@
1
1
  // SharedTypes.ts
2
2
  import type { CodegenTypes } from 'react-native';
3
3
 
4
- /** Shared open/closed control state. */
4
+ /** Shared "open/closed" control state. */
5
5
  export type Visible = 'open' | 'closed';
6
6
 
7
+ /** Shared presentation mode for pickers/menus. */
8
+ export type Presentation = 'modal' | 'embedded';
9
+
7
10
  /** Shared Material preference (Android). */
8
11
  export type AndroidMaterialMode = 'system' | 'm3';
9
12