react-native-tinyui 1.0.0 → 1.1.0

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
@@ -1,16 +1,47 @@
1
+ <img src="./assets/banner.png" alt="TinyUI — Native iOS components for React Native" width="100%" />
2
+
1
3
  # react-native-tinyui
2
4
 
3
- Dependency-free, native iOS `Menu`, `Popover`, `TipKit`, `Stepper`, `ConcentricView`, `SFSymbol` and `LiquidGlassText` components for React Native's
4
- New Architecture. The public API follows normal React composition patterns;
5
- the native UI is exposed through Fabric components. `Menu`, `Popover`, `Stepper`, `ConcentricView` and `SFSymbol` are
6
- implemented directly with UIKit; `LiquidGlassText` uses SwiftUI where needed.
5
+ A dependency-free collection of native iOS components for React Native's New
6
+ Architecture. Built on Fabric, TinyUI offers familiar React composition while
7
+ staying close to native APIs. It uses UIKit by default and SwiftUI where needed.
8
+
9
+ ## Components
10
+
11
+ | Component | What it does |
12
+ | ---------------------------------- | -------------------------------------------------------------- |
13
+ | [Menu](#menu) | Present native menus with actions, submenus, and sections |
14
+ | [Popover](#popover) | Show interactive React Native content in a native popover |
15
+ | [TipKit](#tipkit) | Present contextual tips using Apple's TipKit framework |
16
+ | [LiquidGlassText](#liquidglasstext) | Render text with native glass effects inside the glyphs |
17
+ | [Stepper](#stepper) | Adjust numeric values with the native iOS stepper |
18
+ | [ConcentricView](#concentricview) | Follow the containing view's corners with concentric rounding |
19
+ | [SFSymbol](#sfsymbol) | Display SF Symbols with native rendering and animation effects |
7
20
 
8
- The initial component behavior is inspired by
9
- [`@expo/ui`](https://github.com/expo/expo/tree/main/packages/expo-ui), but this
10
- package does not require Expo Modules or any other runtime package. The
11
- React Native ↔ native view integration follows the approach used by
12
- [`react-native-pager-view`](https://github.com/callstack/react-native-pager-view):
13
- a Fabric component owns the platform view that renders its content.
21
+ Screenshots below show the example app on an iPhone 17 Pro simulator running
22
+ iOS 26.5.
23
+
24
+ ## Philosophy and comparison with `@expo/ui`
25
+
26
+ TinyUI prioritizes performance, stability, a small app footprint, and familiar
27
+ React composition. These priorities guide which components we add and how we
28
+ bridge them.
29
+
30
+ | Design choice | `react-native-tinyui` | `@expo/ui` |
31
+ | --- | --- | --- |
32
+ | Runtime dependencies | Zero additional runtime dependencies beyond `react` and `react-native`. No `expo`, Expo Modules, or `react-native-nitro-modules` required. | Uses Expo Modules; existing React Native apps must [install `expo`](https://docs.expo.dev/versions/latest/sdk/ui/swift-ui/#installation). |
33
+ | Native implementation | Performance and stability first. Prefers direct UIKit integration through Fabric; uses SwiftUI when a component needs it. | Uses [SwiftUI on iOS](https://docs.expo.dev/versions/latest/sdk/ui/swift-ui/), with its layout and hosting model. |
34
+ | Selective inclusion and app size | Component-specific JS imports avoid loading unrelated component modules. An explicit native component list excludes unselected UI implementations and their framework requirements from the pod. | Provides platform-specific JS entry points. Its [iOS podspec](https://github.com/expo/expo/blob/main/packages/expo-ui/ios/ExpoUI.podspec) includes native sources together, without a per-component selection list. JS imports alone do not select native sources. |
35
+ | Component scope | Welcomes both system UI and third-party UI, such as [GlassText](#liquidglasstext). Adding components to the catalog does not require apps to include their UI implementations when they remain unselected. | Centers on SwiftUI and Jetpack Compose. Supports [custom SwiftUI components and modifiers, including third-party SwiftUI libraries](https://docs.expo.dev/guides/expo-ui-swift-ui/extending/). |
36
+ | React API | Familiar props, children, and controlled/uncontrolled state, composed with ordinary React Native views. Names and behavior stay close to the underlying native APIs. | Exposes native toolkit concepts through React, including SwiftUI views, modifiers, and a [`Host` container](https://docs.expo.dev/versions/latest/sdk/ui/swift-ui/#usage). |
37
+ | Supported targets and maintenance | iOS 17+ and the New Architecture only. Excluding older iOS versions and the legacy bridge reduces compatibility code and keeps maintenance focused. | Covers a broader platform scope: SwiftUI on iOS, Jetpack Compose on Android, and [universal components for iOS, Android, and web](https://docs.expo.dev/versions/latest/sdk/ui/universal/). Requirements depend on the Expo SDK and component. |
38
+
39
+ For the smallest builds, combine component-specific JS imports with
40
+ [`react-native-tinyui.components`](#optional-component-selection). All native
41
+ components are enabled by default, so configure that list explicitly. Shared
42
+ core code, Fabric Codegen, and lightweight registration stubs remain in the
43
+ native build; the size savings come from excluding unused UI implementations.
44
+ Final bundle size also depends on the app's bundler and linker settings.
14
45
 
15
46
  ## Requirements
16
47
 
@@ -74,6 +105,8 @@ compatibility. JavaScript entry points provide cleaner dependency boundaries;
74
105
 
75
106
  ## Menu
76
107
 
108
+ <img src="docs/screenshots/menu.png" alt="Menu with native document actions expanded" width="320" />
109
+
77
110
  `Menu` uses `children` as its trigger and receives its native menu entries
78
111
  through the `options` prop. A tap opens the native menu; `onActionPress`
79
112
  receives every selected action's stable id and displayed title.
@@ -132,6 +165,32 @@ or `type: 'divider'` for structural entries; nested entries use their own
132
165
  `options` array. SF Symbols are passed with `systemImage`; missing symbols
133
166
  simply render no image.
134
167
 
168
+ To open the menu from another control, call `open()` on its ref:
169
+
170
+ ```tsx
171
+ import { useRef } from 'react';
172
+ import { Button, Text } from 'react-native';
173
+ import { Menu, type MenuRef } from 'react-native-tinyui/menu';
174
+
175
+ function DocumentMenu() {
176
+ const menuRef = useRef<MenuRef>(null);
177
+
178
+ return (
179
+ <>
180
+ <Menu ref={menuRef} options={[{ id: 'rename', title: 'Rename' }]}>
181
+ <Text>Document actions</Text>
182
+ </Menu>
183
+ <Button title="Open menu" onPress={() => menuRef.current?.open()} />
184
+ </>
185
+ );
186
+ }
187
+ ```
188
+
189
+ `open()` requires iOS 17.4 or newer and anchors the menu to its mounted trigger.
190
+ It does nothing on older iOS versions, while `disabled`, or when the trigger is
191
+ detached. Tapping the trigger still works on all supported iOS versions. The ref
192
+ also retains the outer View's methods, such as `measure` and `measureInWindow`.
193
+
135
194
  Action options support `subtitle`, `state`, `destructive`, `disabled`, `hidden`,
136
195
  and `keepOpen`. Set explicit `id` values when handling `onActionPress`; actions
137
196
  without one receive a generated menu-path id. Use `icon` to load an image from
@@ -144,6 +203,8 @@ plus `destructive`, `disabled`, `hidden`, and `displayInline`.
144
203
 
145
204
  ## Popover
146
205
 
206
+ <img src="docs/screenshots/popover.png" alt="Popover displaying resizable content above its trigger" width="320" />
207
+
147
208
  `Popover` supports standard controlled and uncontrolled React state. Its content
148
209
  remains a normal interactive React Native view tree hosted by a real
149
210
  `UIPopoverPresentationController`.
@@ -189,6 +250,8 @@ outer container, not the presented content.
189
250
 
190
251
  ## TipKit
191
252
 
253
+ <img src="docs/screenshots/tip-kit.png" alt="TipKit popover with a save action anchored to the favorite button" width="320" />
254
+
192
255
  `TipKit.Popover` anchors a native TipKit `TipUIPopoverViewController` to ordinary
193
256
  React Native children on iOS 17+. The children retain their own touch handlers;
194
257
  the tip appears automatically when both `enabled` and TipKit's eligibility allow
@@ -288,6 +351,8 @@ ID for trying the behavior again without clearing the app's TipKit datastore.
288
351
 
289
352
  ## LiquidGlassText
290
353
 
354
+ <img src="docs/screenshots/liquid-glass-text.png" alt="LiquidGlassText rendering English, Chinese, and numbers over a photo" width="320" />
355
+
291
356
  `LiquidGlassText` renders native glass inside the glyph outlines, based on
292
357
  [GlassText](https://github.com/ailtonvivaz/GlassText). It exposes the effect and
293
358
  typography through React props.
@@ -354,6 +419,8 @@ The adapted Core Text outline implementation retains the upstream MIT notice in
354
419
 
355
420
  ## Stepper
356
421
 
422
+ <img src="docs/screenshots/stepper.png" alt="Native Stepper with a controlled value and configurable range" width="320" />
423
+
357
424
  `Stepper` bridges UIKit's
358
425
  [`UIStepper`](https://developer.apple.com/documentation/uikit/uistepper) through
359
426
  Fabric. It supports fractional steps, press-and-hold repeat, and wrapping at
@@ -412,6 +479,8 @@ stepper shows the minus and plus buttons; render a separate `Text` for the value
412
479
 
413
480
  ## ConcentricView
414
481
 
482
+ <img src="docs/screenshots/concentric-view.png" alt="Nested ConcentricView containers following the bottom screen corners" width="320" />
483
+
415
484
  `ConcentricView` is a Fabric container that lets UIKit resolve its corner radii
416
485
  relative to its containing view. On iOS 26+, it applies
417
486
  [`UICornerRadius.containerConcentricRadius`](https://developer.apple.com/documentation/uikit/uicornerradius-c.class/containerconcentricradius)
@@ -447,6 +516,8 @@ concentric radii; keep those decorations on a surrounding view when needed.
447
516
 
448
517
  ## SFSymbol
449
518
 
519
+ <img src="docs/screenshots/sf-symbol.png" alt="SFSymbol rendering a bell badge with native symbol colors" width="320" />
520
+
450
521
  `SFSymbol` uses **UIKit `UIImageView` + Apple's Symbols framework**, exposed
451
522
  through Fabric. It does not embed a SwiftUI hosting view. Xcode 26+ is required
452
523
  to build; the deployment target remains iOS 17. Yoga measures the configured
@@ -559,6 +630,46 @@ multicolor and variable/draw support depend on each symbol's native annotations.
559
630
  transitions are suppressed while Reduce Motion is enabled, while scale and
560
631
  visibility state changes apply instantly. Set it to `false` to opt out.
561
632
 
633
+ ## Example App
634
+
635
+ The [example app](example/src/App.tsx) demonstrates Menu, Popover, TipKit,
636
+ LiquidGlassText, Stepper, ConcentricView, and SFSymbol. It includes all native
637
+ components, so building it requires Xcode 26+. Use iOS 26+ to try the glass effects
638
+ and the latest SF Symbol features.
639
+
640
+ From the repository root, install the dependencies:
641
+
642
+ ```sh
643
+ yarn
644
+ ```
645
+
646
+ Install the example's Pods:
647
+
648
+ ```sh
649
+ cd example/ios && pod install
650
+ ```
651
+
652
+ Then run Metro and the iOS app from the repository root in separate terminals:
653
+
654
+ ```sh
655
+ yarn example start
656
+ ```
657
+
658
+ ```sh
659
+ yarn example ios
660
+ ```
661
+
662
+ The example uses Metro port `8082`, which is already configured in these scripts.
663
+ See the [development workflow](CONTRIBUTING.md#development-workflow) for more
664
+ details.
665
+
666
+ ## Apps Using This Library
667
+
668
+ - [Night Vision - LiDAR Camera](https://apps.apple.com/app/id1668629667)
669
+ - [Laser Measure - LiDAR Powered](https://apps.apple.com/app/id6466744678)
670
+ - [PhoneAway - Digital Detox](https://apps.apple.com/app/id6744548607)
671
+ - [Fatigue Alert - Stay Awake](https://apps.apple.com/app/id6479893638)
672
+
562
673
  ## Contributing
563
674
 
564
675
  - [Development workflow](CONTRIBUTING.md#development-workflow)
@@ -1,4 +1,5 @@
1
1
  import UIKit
2
+ import UIKit.UIGestureRecognizerSubclass
2
3
 
3
4
  /**
4
5
  * UIButton-backed UIKit menu trigger used as the Fabric component's content
@@ -6,6 +7,7 @@ import UIKit
6
7
  */
7
8
  @objc public final class TinyuiMenuProvider: UIButton {
8
9
  private weak var delegate: TinyuiMenuViewDelegate?
10
+ private weak var menuTouchGate: TinyuiMenuTouchGate?
9
11
 
10
12
  @objc public var triggerView: UIView? {
11
13
  didSet {
@@ -35,6 +37,32 @@ import UIKit
35
37
  configure()
36
38
  }
37
39
 
40
+ override public func didMoveToWindow() {
41
+ super.didMoveToWindow()
42
+ menuTouchGate?.presentedMenus.remove(self)
43
+ menuTouchGate = window.map { TinyuiMenuTouchGate.install(in: $0) }
44
+ }
45
+
46
+ override public func contextMenuInteraction(
47
+ _ interaction: UIContextMenuInteraction,
48
+ willDisplayMenuFor configuration: UIContextMenuConfiguration,
49
+ animator: UIContextMenuInteractionAnimating?
50
+ ) {
51
+ super.contextMenuInteraction(interaction, willDisplayMenuFor: configuration, animator: animator)
52
+ menuTouchGate?.presentedMenus.add(self)
53
+ }
54
+
55
+ override public func contextMenuInteraction(
56
+ _ interaction: UIContextMenuInteraction,
57
+ willEndFor configuration: UIContextMenuConfiguration,
58
+ animator: UIContextMenuInteractionAnimating?
59
+ ) {
60
+ super.contextMenuInteraction(interaction, willEndFor: configuration, animator: animator)
61
+ // The dismissal touch has already been filtered. New touches during the
62
+ // closing animation belong to the page again.
63
+ menuTouchGate?.presentedMenus.remove(self)
64
+ }
65
+
38
66
  override public func layoutSubviews() {
39
67
  super.layoutSubviews()
40
68
  triggerView?.frame = bounds
@@ -56,6 +84,16 @@ import UIKit
56
84
  showsMenuAsPrimaryAction = true
57
85
  }
58
86
 
87
+ @objc public func openMenu() {
88
+ guard isEnabled, isUserInteractionEnabled, !isHidden, alpha > 0.01,
89
+ window != nil, !bounds.isEmpty, menu != nil else {
90
+ return
91
+ }
92
+ if #available(iOS 17.4, *) {
93
+ performPrimaryAction()
94
+ }
95
+ }
96
+
59
97
  /** Applies one complete Fabric props snapshot before rebuilding the menu. */
60
98
  @objc public func update(
61
99
  menuConfig: [String: Any],
@@ -80,3 +118,65 @@ import UIKit
80
118
  )
81
119
  }
82
120
  }
121
+
122
+ /**
123
+ * Uses the early-event filtering approach from Expo's SystemMenuTouchGate:
124
+ * https://github.com/expo/expo/pull/49775
125
+ * TinyUI tracks its own menus through public UIButton lifecycle callbacks.
126
+ */
127
+ final class TinyuiMenuTouchGate: UIGestureRecognizer, UIGestureRecognizerDelegate {
128
+ let presentedMenus = NSHashTable<TinyuiMenuProvider>.weakObjects()
129
+ private static let surfaceTouchHandlerClass = NSClassFromString("RCTSurfaceTouchHandler")
130
+
131
+ init() {
132
+ super.init(target: nil, action: nil)
133
+ cancelsTouchesInView = false
134
+ delaysTouchesBegan = false
135
+ delaysTouchesEnded = false
136
+ delegate = self
137
+ }
138
+
139
+ static func install(in window: UIWindow) -> TinyuiMenuTouchGate {
140
+ if let gate = window.gestureRecognizers?.first(where: { $0 is TinyuiMenuTouchGate })
141
+ as? TinyuiMenuTouchGate {
142
+ return gate
143
+ }
144
+ let gate = TinyuiMenuTouchGate()
145
+ window.addGestureRecognizer(gate)
146
+ return gate
147
+ }
148
+
149
+ private func ignoreReactTouches(_ touches: Set<UITouch>, event: UIEvent) {
150
+ guard let window = view as? UIWindow,
151
+ presentedMenus.allObjects.contains(where: { $0.window === window }),
152
+ let handlerClass = Self.surfaceTouchHandlerClass else {
153
+ return
154
+ }
155
+
156
+ for touch in touches where touch.phase == .began {
157
+ var ancestor = touch.view
158
+ while let current = ancestor {
159
+ for handler in current.gestureRecognizers ?? [] where handler.isKind(of: handlerClass) {
160
+ handler.ignore(touch, for: event)
161
+ }
162
+ ancestor = current.superview
163
+ }
164
+ }
165
+ }
166
+
167
+ func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive event: UIEvent) -> Bool {
168
+ // Filtering in touchesBegan alone is too late: RN may already have sent
169
+ // touchStart/onPressIn to JS. UIKit asks this delegate before delivery.
170
+ if let touches = event.allTouches {
171
+ ignoreReactTouches(touches, event: event)
172
+ }
173
+ return true
174
+ }
175
+
176
+ override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
177
+ super.touchesBegan(touches, with: event)
178
+ // Fallback for delivery orders where RN sees the touch first.
179
+ ignoreReactTouches(touches, event: event)
180
+ state = .failed
181
+ }
182
+ }
@@ -64,6 +64,18 @@ using namespace facebook::react;
64
64
  }
65
65
  }
66
66
 
67
+ #pragma mark - Commands
68
+
69
+ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
70
+ {
71
+ RCTTinyuiMenuViewHandleCommand(self, commandName, args);
72
+ }
73
+
74
+ - (void)open
75
+ {
76
+ [_menuProvider openMenu];
77
+ }
78
+
67
79
  #pragma mark - Props
68
80
 
69
81
  - (void)updateProps:(Props::Shared const &)props
@@ -1,6 +1,9 @@
1
+ import type * as React from 'react';
1
2
  import {
2
3
  codegenNativeComponent,
4
+ codegenNativeCommands,
3
5
  type CodegenTypes,
6
+ type HostComponent,
4
7
  type ViewProps,
5
8
  } from 'react-native';
6
9
  import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes';
@@ -12,6 +15,14 @@ export interface NativeMenuProps extends ViewProps {
12
15
  onActionPress?: DirectEventHandler<Readonly<{ id: string; title: string }>>;
13
16
  }
14
17
 
18
+ interface NativeCommands {
19
+ open: (viewRef: React.ComponentRef<HostComponent<NativeMenuProps>>) => void;
20
+ }
21
+
22
+ export const Commands = codegenNativeCommands<NativeCommands>({
23
+ supportedCommands: ['open'],
24
+ });
25
+
15
26
  export default codegenNativeComponent<NativeMenuProps>('TinyuiMenuView', {
16
27
  excludedPlatforms: ['android'],
17
28
  });
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
 
3
- import { forwardRef } from 'react';
3
+ import { forwardRef, useImperativeHandle, useRef } from 'react';
4
4
  import { View } from 'react-native';
5
5
  import { assertComponentEnabled } from "../utils/index.js";
6
- import NativeMenuView from './MenuNativeComponent';
6
+ import NativeMenuView, { Commands } from './MenuNativeComponent';
7
7
  import { serializeMenuOptions } from "./options.js";
8
8
  import { jsx as _jsx } from "react/jsx-runtime";
9
9
  const MenuRoot = /*#__PURE__*/forwardRef(function Menu({
@@ -17,12 +17,22 @@ const MenuRoot = /*#__PURE__*/forwardRef(function Menu({
17
17
  ...viewProps
18
18
  }, ref) {
19
19
  assertComponentEnabled('Menu');
20
+ const viewRef = useRef(null);
21
+ const nativeRef = useRef(null);
22
+ useImperativeHandle(ref, () => Object.assign(viewRef.current, {
23
+ open() {
24
+ if (!disabled && nativeRef.current) {
25
+ Commands.open(nativeRef.current);
26
+ }
27
+ }
28
+ }), [disabled]);
20
29
  const menuConfig = serializeMenuOptions(options);
21
30
  return /*#__PURE__*/_jsx(View, {
22
31
  ...viewProps,
23
- ref: ref,
32
+ ref: viewRef,
24
33
  collapsable: false,
25
34
  children: /*#__PURE__*/_jsx(NativeMenuView, {
35
+ ref: nativeRef,
26
36
  accessibilityLabel: accessibilityLabel,
27
37
  disabled: disabled,
28
38
  menuConfig: menuConfig,
@@ -1 +1 @@
1
- {"version":3,"names":["forwardRef","View","assertComponentEnabled","NativeMenuView","serializeMenuOptions","jsx","_jsx","MenuRoot","Menu","accessibilityLabel","children","disabled","onActionPress","options","title","testID","viewProps","ref","menuConfig","collapsable"],"sourceRoot":"../../../src","sources":["Menu/index.tsx"],"mappings":";;AAAA,SAASA,UAAU,QAA2B,OAAO;AACrD,SAASC,IAAI,QAAQ,cAAc;AAEnC,SAASC,sBAAsB,QAAQ,mBAAU;AAEjD,OAAOC,cAAc,MAAM,uBAAuB;AAClD,SAASC,oBAAoB,QAAQ,cAAW;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAEjD,MAAMC,QAAQ,gBAAGP,UAAU,CAAuC,SAASQ,IAAIA,CAC7E;EACEC,kBAAkB;EAClBC,QAAQ;EACRC,QAAQ;EACRC,aAAa;EACbC,OAAO;EACPC,KAAK;EACLC,MAAM;EACN,GAAGC;AACL,CAAC,EACDC,GAAG,EACH;EACAf,sBAAsB,CAAC,MAAM,CAAC;EAE9B,MAAMgB,UAAU,GAAGd,oBAAoB,CAACS,OAAO,CAAC;EAEhD,oBACEP,IAAA,CAACL,IAAI;IAAA,GAAKe,SAAS;IAAEC,GAAG,EAAEA,GAAI;IAACE,WAAW,EAAE,KAAM;IAAAT,QAAA,eAChDJ,IAAA,CAACH,cAAc;MACbM,kBAAkB,EAAEA,kBAAmB;MACvCE,QAAQ,EAAEA,QAAS;MACnBO,UAAU,EAAEA,UAAW;MACvBN,aAAa,EAAEA,aAAc;MAC7BG,MAAM,EAAEA,MAAO;MACfD,KAAK,EAAEA,KAAM;MAAAJ,QAAA,eAEbJ,IAAA,CAACL,IAAI;QAACkB,WAAW,EAAE,KAAM;QAAAT,QAAA,EAAEA;MAAQ,CAAO;IAAC,CAC7B;EAAC,CACb,CAAC;AAEX,CAAC,CAAC;AAEF,OAAO,MAAMF,IAAI,GAAGD,QAAyB","ignoreList":[]}
1
+ {"version":3,"names":["forwardRef","useImperativeHandle","useRef","View","assertComponentEnabled","NativeMenuView","Commands","serializeMenuOptions","jsx","_jsx","MenuRoot","Menu","accessibilityLabel","children","disabled","onActionPress","options","title","testID","viewProps","ref","viewRef","nativeRef","Object","assign","current","open","menuConfig","collapsable"],"sourceRoot":"../../../src","sources":["Menu/index.tsx"],"mappings":";;AAAA,SACEA,UAAU,EACVC,mBAAmB,EACnBC,MAAM,QAED,OAAO;AACd,SAASC,IAAI,QAA2B,cAAc;AAEtD,SAASC,sBAAsB,QAAQ,mBAAU;AAEjD,OAAOC,cAAc,IAAIC,QAAQ,QAAQ,uBAAuB;AAChE,SAASC,oBAAoB,QAAQ,cAAW;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAEjD,MAAMC,QAAQ,gBAAGV,UAAU,CAAqB,SAASW,IAAIA,CAC3D;EACEC,kBAAkB;EAClBC,QAAQ;EACRC,QAAQ;EACRC,aAAa;EACbC,OAAO;EACPC,KAAK;EACLC,MAAM;EACN,GAAGC;AACL,CAAC,EACDC,GAAG,EACH;EACAhB,sBAAsB,CAAC,MAAM,CAAC;EAE9B,MAAMiB,OAAO,GAAGnB,MAAM,CAAe,IAAI,CAAC;EAC1C,MAAMoB,SAAS,GAAGpB,MAAM,CAAsC,IAAI,CAAC;EAEnED,mBAAmB,CACjBmB,GAAG,EACH,MACEG,MAAM,CAACC,MAAM,CAACH,OAAO,CAACI,OAAO,EAAG;IAC9BC,IAAIA,CAAA,EAAG;MACL,IAAI,CAACZ,QAAQ,IAAIQ,SAAS,CAACG,OAAO,EAAE;QAClCnB,QAAQ,CAACoB,IAAI,CAACJ,SAAS,CAACG,OAAO,CAAC;MAClC;IACF;EACF,CAAC,CAAC,EACJ,CAACX,QAAQ,CACX,CAAC;EAED,MAAMa,UAAU,GAAGpB,oBAAoB,CAACS,OAAO,CAAC;EAEhD,oBACEP,IAAA,CAACN,IAAI;IAAA,GAAKgB,SAAS;IAAEC,GAAG,EAAEC,OAAQ;IAACO,WAAW,EAAE,KAAM;IAAAf,QAAA,eACpDJ,IAAA,CAACJ,cAAc;MACbe,GAAG,EAAEE,SAAU;MACfV,kBAAkB,EAAEA,kBAAmB;MACvCE,QAAQ,EAAEA,QAAS;MACnBa,UAAU,EAAEA,UAAW;MACvBZ,aAAa,EAAEA,aAAc;MAC7BG,MAAM,EAAEA,MAAO;MACfD,KAAK,EAAEA,KAAM;MAAAJ,QAAA,eAEbJ,IAAA,CAACN,IAAI;QAACyB,WAAW,EAAE,KAAM;QAAAf,QAAA,EAAEA;MAAQ,CAAO;IAAC,CAC7B;EAAC,CACb,CAAC;AAEX,CAAC,CAAC;AAEF,OAAO,MAAMF,IAAI,GAAGD,QAAyB","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["Menu","TipKit","Stepper","ConcentricView","SFSymbol","Popover","PopoverClose","getNativeTinyui","assertComponentEnabled","LiquidGlassText"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,IAAI,QAAQ,iBAAQ;AAC7B,SAASC,MAAM,QAAQ,mBAAU;AAsBjC,SAASC,OAAO,QAAQ,oBAAW;AAGnC,SAASC,cAAc,QAAQ,2BAAkB;AAGjD,SAASC,QAAQ,QAAQ,qBAAY;AAYrC,SAASC,OAAO,EAAEC,YAAY,QAAQ,oBAAW;AAUjD,SAASC,eAAe,EAAEC,sBAAsB,QAAQ,kBAAS;AAGjE,SAASC,eAAe,QAAQ,4BAAmB","ignoreList":[]}
1
+ {"version":3,"names":["Menu","TipKit","Stepper","ConcentricView","SFSymbol","Popover","PopoverClose","getNativeTinyui","assertComponentEnabled","LiquidGlassText"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,IAAI,QAAQ,iBAAQ;AAC7B,SAASC,MAAM,QAAQ,mBAAU;AAuBjC,SAASC,OAAO,QAAQ,oBAAW;AAGnC,SAASC,cAAc,QAAQ,2BAAkB;AAGjD,SAASC,QAAQ,QAAQ,qBAAY;AAYrC,SAASC,OAAO,EAAEC,YAAY,QAAQ,oBAAW;AAUjD,SAASC,eAAe,EAAEC,sBAAsB,QAAQ,kBAAS;AAGjE,SAASC,eAAe,QAAQ,4BAAmB","ignoreList":[]}
@@ -1,4 +1,5 @@
1
- import { type CodegenTypes, type ViewProps } from 'react-native';
1
+ import type * as React from 'react';
2
+ import { type CodegenTypes, type HostComponent, type ViewProps } from 'react-native';
2
3
  import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes';
3
4
  export interface NativeMenuProps extends ViewProps {
4
5
  menuConfig: CodegenTypes.UnsafeMixed;
@@ -9,6 +10,10 @@ export interface NativeMenuProps extends ViewProps {
9
10
  title: string;
10
11
  }>>;
11
12
  }
13
+ interface NativeCommands {
14
+ open: (viewRef: React.ComponentRef<HostComponent<NativeMenuProps>>) => void;
15
+ }
16
+ export declare const Commands: NativeCommands;
12
17
  declare const _default: import("react-native/types_generated/Libraries/Utilities/codegenNativeComponent").NativeComponentType<NativeMenuProps>;
13
18
  export default _default;
14
19
  //# sourceMappingURL=MenuNativeComponent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MenuNativeComponent.d.ts","sourceRoot":"","sources":["../../../../src/Menu/MenuNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAEpF,MAAM,WAAW,eAAgB,SAAQ,SAAS;IAChD,UAAU,EAAE,YAAY,CAAC,WAAW,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;CAC7E;;AAED,wBAEG"}
1
+ {"version":3,"file":"MenuNativeComponent.d.ts","sourceRoot":"","sources":["../../../../src/Menu/MenuNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAEpF,MAAM,WAAW,eAAgB,SAAQ,SAAS;IAChD,UAAU,EAAE,YAAY,CAAC,WAAW,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;CAC7E;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,KAAK,IAAI,CAAC;CAC7E;AAED,eAAO,MAAM,QAAQ,gBAEnB,CAAC;;AAEH,wBAEG"}
@@ -1,4 +1,4 @@
1
1
  import type { MenuComponent } from './types.js';
2
2
  export declare const Menu: MenuComponent;
3
- export type { MenuActionOption, MenuActionPressEvent, MenuActionPressEventData, MenuDividerOption, MenuItemState, MenuOption, MenuProps, MenuSectionOption, MenuSubmenuOption, } from './types.js';
3
+ export type { MenuActionOption, MenuActionPressEvent, MenuActionPressEventData, MenuDividerOption, MenuItemState, MenuOption, MenuProps, MenuRef, MenuSectionOption, MenuSubmenuOption, } from './types.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/Menu/index.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAa,MAAM,YAAS,CAAC;AAqCxD,eAAO,MAAM,IAAI,EAAe,aAAa,CAAC;AAE9C,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,YAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/Menu/index.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,YAAS,CAAC;AAsDjE,eAAO,MAAM,IAAI,EAAe,aAAa,CAAC;AAE9C,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,SAAS,EACT,OAAO,EACP,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,YAAS,CAAC"}
@@ -1,5 +1,5 @@
1
- import type { ComponentRef, ForwardRefExoticComponent, ReactNode, RefAttributes } from 'react';
2
- import type { ColorValue, View, ViewProps } from 'react-native';
1
+ import type { ForwardRefExoticComponent, ReactNode, RefAttributes } from 'react';
2
+ import type { ColorValue, HostInstance, ViewProps } from 'react-native';
3
3
  export type MenuItemState = 'off' | 'on' | 'mixed';
4
4
  interface MenuOptionLabel {
5
5
  title: string;
@@ -59,6 +59,13 @@ export interface MenuActionPressEventData {
59
59
  export type MenuActionPressEvent = Readonly<{
60
60
  nativeEvent: Readonly<MenuActionPressEventData>;
61
61
  }>;
62
+ export type MenuRef = HostInstance & {
63
+ /**
64
+ * Opens the menu at its mounted trigger on iOS 17.4 and newer.
65
+ * Does nothing on older iOS versions or while disabled or detached.
66
+ */
67
+ open(): void;
68
+ };
62
69
  export interface MenuProps extends Omit<ViewProps, 'children'> {
63
70
  /** The trigger element. */
64
71
  children: ReactNode;
@@ -71,7 +78,7 @@ export interface MenuProps extends Omit<ViewProps, 'children'> {
71
78
  /** Called whenever an enabled action is selected. */
72
79
  onActionPress?: (event: MenuActionPressEvent) => void;
73
80
  }
74
- export interface MenuComponent extends ForwardRefExoticComponent<MenuProps & RefAttributes<ComponentRef<typeof View>>> {
81
+ export interface MenuComponent extends ForwardRefExoticComponent<MenuProps & RefAttributes<MenuRef>> {
75
82
  }
76
83
  export {};
77
84
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/Menu/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,yBAAyB,EACzB,SAAS,EACT,aAAa,EACd,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEhE,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC;AAEnD,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,mEAAmE;IACnE,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,gEAAgE;IAChE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,IAAI,EAAE,SAAS,CAAC;IAChB,yDAAyD;IACzD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kDAAkD;IAClD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,MAAM,UAAU,GACpB,gBAAgB,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;AAE/E,MAAM,WAAW,wBAAwB;IACvC,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAC;IACX,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,WAAW,EAAE,QAAQ,CAAC,wBAAwB,CAAC,CAAC;CACjD,CAAC,CAAC;AAEH,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IAC5D,2BAA2B;IAC3B,QAAQ,EAAE,SAAS,CAAC;IACpB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;IAC/B,kDAAkD;IAClD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qDAAqD;IACrD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACvD;AAED,MAAM,WAAW,aAAc,SAAQ,yBAAyB,CAC9D,SAAS,GAAG,aAAa,CAAC,YAAY,CAAC,OAAO,IAAI,CAAC,CAAC,CACrD;CAAG"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/Menu/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,SAAS,EACT,aAAa,EACd,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAExE,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC;AAEnD,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,mEAAmE;IACnE,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,gEAAgE;IAChE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,IAAI,EAAE,SAAS,CAAC;IAChB,yDAAyD;IACzD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kDAAkD;IAClD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,MAAM,UAAU,GACpB,gBAAgB,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;AAE/E,MAAM,WAAW,wBAAwB;IACvC,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAC;IACX,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,WAAW,EAAE,QAAQ,CAAC,wBAAwB,CAAC,CAAC;CACjD,CAAC,CAAC;AAEH,MAAM,MAAM,OAAO,GAAG,YAAY,GAAG;IACnC;;;OAGG;IACH,IAAI,IAAI,IAAI,CAAC;CACd,CAAC;AAEF,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IAC5D,2BAA2B;IAC3B,QAAQ,EAAE,SAAS,CAAC;IACpB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;IAC/B,kDAAkD;IAClD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qDAAqD;IACrD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACvD;AAED,MAAM,WAAW,aAAc,SAAQ,yBAAyB,CAC9D,SAAS,GAAG,aAAa,CAAC,OAAO,CAAC,CACnC;CAAG"}
@@ -1,7 +1,7 @@
1
1
  export { Menu } from './Menu/index.js';
2
2
  export { TipKit } from './TipKit/index.js';
3
3
  export type { TipAction, TipDisplayFrequency, TipError, TipInvalidationReason, TipPopoverProps, TipKitConfiguration, TipStatus, } from './TipKit/index.js';
4
- export type { MenuActionOption, MenuActionPressEvent, MenuActionPressEventData, MenuDividerOption, MenuItemState, MenuOption, MenuProps, MenuSectionOption, MenuSubmenuOption, } from './Menu/index.js';
4
+ export type { MenuActionOption, MenuActionPressEvent, MenuActionPressEventData, MenuDividerOption, MenuItemState, MenuOption, MenuProps, MenuRef, MenuSectionOption, MenuSubmenuOption, } from './Menu/index.js';
5
5
  export { Stepper } from './Stepper/index.js';
6
6
  export type { StepperProps } from './Stepper/index.js';
7
7
  export { ConcentricView } from './ConcentricView/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAQ,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAU,CAAC;AAClC,YAAY,EACV,SAAS,EACT,mBAAmB,EACnB,QAAQ,EACR,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,SAAS,GACV,MAAM,mBAAU,CAAC;AAClB,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,iBAAQ,CAAC;AAEhB,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAW,CAAC;AACpC,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAW,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,2BAAkB,CAAC;AAClD,YAAY,EAAE,mBAAmB,EAAE,MAAM,2BAAkB,CAAC;AAE5D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAY,CAAC;AACtC,YAAY,EACV,yBAAyB,EACzB,cAAc,EACd,qBAAqB,EACrB,aAAa,EACb,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,cAAc,GACf,MAAM,qBAAY,CAAC;AAEpB,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAW,CAAC;AAClD,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,cAAc,EACd,wBAAwB,EACxB,YAAY,GACb,MAAM,oBAAW,CAAC;AAEnB,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,kBAAS,CAAC;AAClE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAS,CAAC;AAE/C,OAAO,EAAE,eAAe,EAAE,MAAM,4BAAmB,CAAC;AACpD,YAAY,EACV,yBAAyB,EACzB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,4BAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAQ,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAU,CAAC;AAClC,YAAY,EACV,SAAS,EACT,mBAAmB,EACnB,QAAQ,EACR,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,SAAS,GACV,MAAM,mBAAU,CAAC;AAClB,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,SAAS,EACT,OAAO,EACP,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,iBAAQ,CAAC;AAEhB,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAW,CAAC;AACpC,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAW,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,2BAAkB,CAAC;AAClD,YAAY,EAAE,mBAAmB,EAAE,MAAM,2BAAkB,CAAC;AAE5D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAY,CAAC;AACtC,YAAY,EACV,yBAAyB,EACzB,cAAc,EACd,qBAAqB,EACrB,aAAa,EACb,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,cAAc,GACf,MAAM,qBAAY,CAAC;AAEpB,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAW,CAAC;AAClD,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,cAAc,EACd,wBAAwB,EACxB,YAAY,GACb,MAAM,oBAAW,CAAC;AAEnB,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,kBAAS,CAAC;AAClE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAS,CAAC;AAE/C,OAAO,EAAE,eAAe,EAAE,MAAM,4BAAmB,CAAC;AACpD,YAAY,EACV,yBAAyB,EACzB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,4BAAmB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-tinyui",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Dependency-free native iOS Menu, Popover, TipKit, Stepper, ConcentricView, SFSymbol and LiquidGlassText components for React Native",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -1,6 +1,9 @@
1
+ import type * as React from 'react';
1
2
  import {
2
3
  codegenNativeComponent,
4
+ codegenNativeCommands,
3
5
  type CodegenTypes,
6
+ type HostComponent,
4
7
  type ViewProps,
5
8
  } from 'react-native';
6
9
  import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes';
@@ -12,6 +15,14 @@ export interface NativeMenuProps extends ViewProps {
12
15
  onActionPress?: DirectEventHandler<Readonly<{ id: string; title: string }>>;
13
16
  }
14
17
 
18
+ interface NativeCommands {
19
+ open: (viewRef: React.ComponentRef<HostComponent<NativeMenuProps>>) => void;
20
+ }
21
+
22
+ export const Commands = codegenNativeCommands<NativeCommands>({
23
+ supportedCommands: ['open'],
24
+ });
25
+
15
26
  export default codegenNativeComponent<NativeMenuProps>('TinyuiMenuView', {
16
27
  excludedPlatforms: ['android'],
17
28
  });
@@ -1,12 +1,17 @@
1
- import { forwardRef, type ComponentRef } from 'react';
2
- import { View } from 'react-native';
1
+ import {
2
+ forwardRef,
3
+ useImperativeHandle,
4
+ useRef,
5
+ type ComponentRef,
6
+ } from 'react';
7
+ import { View, type HostInstance } from 'react-native';
3
8
 
4
9
  import { assertComponentEnabled } from '../utils';
5
- import type { MenuComponent, MenuProps } from './types';
6
- import NativeMenuView from './MenuNativeComponent';
10
+ import type { MenuComponent, MenuProps, MenuRef } from './types';
11
+ import NativeMenuView, { Commands } from './MenuNativeComponent';
7
12
  import { serializeMenuOptions } from './options';
8
13
 
9
- const MenuRoot = forwardRef<ComponentRef<typeof View>, MenuProps>(function Menu(
14
+ const MenuRoot = forwardRef<MenuRef, MenuProps>(function Menu(
10
15
  {
11
16
  accessibilityLabel,
12
17
  children,
@@ -21,11 +26,28 @@ const MenuRoot = forwardRef<ComponentRef<typeof View>, MenuProps>(function Menu(
21
26
  ) {
22
27
  assertComponentEnabled('Menu');
23
28
 
29
+ const viewRef = useRef<HostInstance>(null);
30
+ const nativeRef = useRef<ComponentRef<typeof NativeMenuView>>(null);
31
+
32
+ useImperativeHandle(
33
+ ref,
34
+ () =>
35
+ Object.assign(viewRef.current!, {
36
+ open() {
37
+ if (!disabled && nativeRef.current) {
38
+ Commands.open(nativeRef.current);
39
+ }
40
+ },
41
+ }),
42
+ [disabled]
43
+ );
44
+
24
45
  const menuConfig = serializeMenuOptions(options);
25
46
 
26
47
  return (
27
- <View {...viewProps} ref={ref} collapsable={false}>
48
+ <View {...viewProps} ref={viewRef} collapsable={false}>
28
49
  <NativeMenuView
50
+ ref={nativeRef}
29
51
  accessibilityLabel={accessibilityLabel}
30
52
  disabled={disabled}
31
53
  menuConfig={menuConfig}
@@ -49,6 +71,7 @@ export type {
49
71
  MenuItemState,
50
72
  MenuOption,
51
73
  MenuProps,
74
+ MenuRef,
52
75
  MenuSectionOption,
53
76
  MenuSubmenuOption,
54
77
  } from './types';
package/src/Menu/types.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import type {
2
- ComponentRef,
3
2
  ForwardRefExoticComponent,
4
3
  ReactNode,
5
4
  RefAttributes,
6
5
  } from 'react';
7
- import type { ColorValue, View, ViewProps } from 'react-native';
6
+ import type { ColorValue, HostInstance, ViewProps } from 'react-native';
8
7
 
9
8
  export type MenuItemState = 'off' | 'on' | 'mixed';
10
9
 
@@ -75,6 +74,14 @@ export type MenuActionPressEvent = Readonly<{
75
74
  nativeEvent: Readonly<MenuActionPressEventData>;
76
75
  }>;
77
76
 
77
+ export type MenuRef = HostInstance & {
78
+ /**
79
+ * Opens the menu at its mounted trigger on iOS 17.4 and newer.
80
+ * Does nothing on older iOS versions or while disabled or detached.
81
+ */
82
+ open(): void;
83
+ };
84
+
78
85
  export interface MenuProps extends Omit<ViewProps, 'children'> {
79
86
  /** The trigger element. */
80
87
  children: ReactNode;
@@ -89,5 +96,5 @@ export interface MenuProps extends Omit<ViewProps, 'children'> {
89
96
  }
90
97
 
91
98
  export interface MenuComponent extends ForwardRefExoticComponent<
92
- MenuProps & RefAttributes<ComponentRef<typeof View>>
99
+ MenuProps & RefAttributes<MenuRef>
93
100
  > {}
package/src/index.tsx CHANGED
@@ -17,6 +17,7 @@ export type {
17
17
  MenuItemState,
18
18
  MenuOption,
19
19
  MenuProps,
20
+ MenuRef,
20
21
  MenuSectionOption,
21
22
  MenuSubmenuOption,
22
23
  } from './Menu';