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 +121 -10
- package/ios/Menu/TinyuiMenuProvider.swift +100 -0
- package/ios/Menu/TinyuiMenuView.mm +12 -0
- package/lib/module/Menu/MenuNativeComponent.ts +11 -0
- package/lib/module/Menu/index.js +13 -3
- package/lib/module/Menu/index.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/Menu/MenuNativeComponent.d.ts +6 -1
- package/lib/typescript/src/Menu/MenuNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/Menu/index.d.ts +1 -1
- package/lib/typescript/src/Menu/index.d.ts.map +1 -1
- package/lib/typescript/src/Menu/types.d.ts +10 -3
- package/lib/typescript/src/Menu/types.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Menu/MenuNativeComponent.ts +11 -0
- package/src/Menu/index.tsx +29 -6
- package/src/Menu/types.ts +10 -3
- package/src/index.tsx +1 -0
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
});
|
package/lib/module/Menu/index.js
CHANGED
|
@@ -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:
|
|
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,
|
|
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":[]}
|
package/lib/module/index.js.map
CHANGED
|
@@ -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;
|
|
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
|
|
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,
|
|
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":"
|
|
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 {
|
|
2
|
-
import type { ColorValue,
|
|
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<
|
|
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,
|
|
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.
|
|
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
|
});
|
package/src/Menu/index.tsx
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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<
|
|
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={
|
|
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,
|
|
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<
|
|
99
|
+
MenuProps & RefAttributes<MenuRef>
|
|
93
100
|
> {}
|