react-native-tinyui 1.0.0 → 1.1.1
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 +125 -10
- package/ios/Menu/TinyuiMenuProvider.swift +100 -0
- package/ios/Menu/TinyuiMenuView.mm +27 -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 |
|
|
20
|
+
|
|
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.
|
|
7
29
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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,36 @@ 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
|
+
The trigger can be nested inside a React Native `Pressable` or `Touchable`.
|
|
169
|
+
Tapping an enabled menu trigger opens the menu without firing the parent's
|
|
170
|
+
touch or press handlers; tapping elsewhere in the parent keeps its normal behavior.
|
|
171
|
+
|
|
172
|
+
To open the menu from another control, call `open()` on its ref:
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
import { useRef } from 'react';
|
|
176
|
+
import { Button, Text } from 'react-native';
|
|
177
|
+
import { Menu, type MenuRef } from 'react-native-tinyui/menu';
|
|
178
|
+
|
|
179
|
+
function DocumentMenu() {
|
|
180
|
+
const menuRef = useRef<MenuRef>(null);
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<>
|
|
184
|
+
<Menu ref={menuRef} options={[{ id: 'rename', title: 'Rename' }]}>
|
|
185
|
+
<Text>Document actions</Text>
|
|
186
|
+
</Menu>
|
|
187
|
+
<Button title="Open menu" onPress={() => menuRef.current?.open()} />
|
|
188
|
+
</>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`open()` requires iOS 17.4 or newer and anchors the menu to its mounted trigger.
|
|
194
|
+
It does nothing on older iOS versions, while `disabled`, or when the trigger is
|
|
195
|
+
detached. Tapping the trigger still works on all supported iOS versions. The ref
|
|
196
|
+
also retains the outer View's methods, such as `measure` and `measureInWindow`.
|
|
197
|
+
|
|
135
198
|
Action options support `subtitle`, `state`, `destructive`, `disabled`, `hidden`,
|
|
136
199
|
and `keepOpen`. Set explicit `id` values when handling `onActionPress`; actions
|
|
137
200
|
without one receive a generated menu-path id. Use `icon` to load an image from
|
|
@@ -144,6 +207,8 @@ plus `destructive`, `disabled`, `hidden`, and `displayInline`.
|
|
|
144
207
|
|
|
145
208
|
## Popover
|
|
146
209
|
|
|
210
|
+
<img src="docs/screenshots/popover.png" alt="Popover displaying resizable content above its trigger" width="320" />
|
|
211
|
+
|
|
147
212
|
`Popover` supports standard controlled and uncontrolled React state. Its content
|
|
148
213
|
remains a normal interactive React Native view tree hosted by a real
|
|
149
214
|
`UIPopoverPresentationController`.
|
|
@@ -189,6 +254,8 @@ outer container, not the presented content.
|
|
|
189
254
|
|
|
190
255
|
## TipKit
|
|
191
256
|
|
|
257
|
+
<img src="docs/screenshots/tip-kit.png" alt="TipKit popover with a save action anchored to the favorite button" width="320" />
|
|
258
|
+
|
|
192
259
|
`TipKit.Popover` anchors a native TipKit `TipUIPopoverViewController` to ordinary
|
|
193
260
|
React Native children on iOS 17+. The children retain their own touch handlers;
|
|
194
261
|
the tip appears automatically when both `enabled` and TipKit's eligibility allow
|
|
@@ -288,6 +355,8 @@ ID for trying the behavior again without clearing the app's TipKit datastore.
|
|
|
288
355
|
|
|
289
356
|
## LiquidGlassText
|
|
290
357
|
|
|
358
|
+
<img src="docs/screenshots/liquid-glass-text.png" alt="LiquidGlassText rendering English, Chinese, and numbers over a photo" width="320" />
|
|
359
|
+
|
|
291
360
|
`LiquidGlassText` renders native glass inside the glyph outlines, based on
|
|
292
361
|
[GlassText](https://github.com/ailtonvivaz/GlassText). It exposes the effect and
|
|
293
362
|
typography through React props.
|
|
@@ -354,6 +423,8 @@ The adapted Core Text outline implementation retains the upstream MIT notice in
|
|
|
354
423
|
|
|
355
424
|
## Stepper
|
|
356
425
|
|
|
426
|
+
<img src="docs/screenshots/stepper.png" alt="Native Stepper with a controlled value and configurable range" width="320" />
|
|
427
|
+
|
|
357
428
|
`Stepper` bridges UIKit's
|
|
358
429
|
[`UIStepper`](https://developer.apple.com/documentation/uikit/uistepper) through
|
|
359
430
|
Fabric. It supports fractional steps, press-and-hold repeat, and wrapping at
|
|
@@ -412,6 +483,8 @@ stepper shows the minus and plus buttons; render a separate `Text` for the value
|
|
|
412
483
|
|
|
413
484
|
## ConcentricView
|
|
414
485
|
|
|
486
|
+
<img src="docs/screenshots/concentric-view.png" alt="Nested ConcentricView containers following the bottom screen corners" width="320" />
|
|
487
|
+
|
|
415
488
|
`ConcentricView` is a Fabric container that lets UIKit resolve its corner radii
|
|
416
489
|
relative to its containing view. On iOS 26+, it applies
|
|
417
490
|
[`UICornerRadius.containerConcentricRadius`](https://developer.apple.com/documentation/uikit/uicornerradius-c.class/containerconcentricradius)
|
|
@@ -447,6 +520,8 @@ concentric radii; keep those decorations on a surrounding view when needed.
|
|
|
447
520
|
|
|
448
521
|
## SFSymbol
|
|
449
522
|
|
|
523
|
+
<img src="docs/screenshots/sf-symbol.png" alt="SFSymbol rendering a bell badge with native symbol colors" width="320" />
|
|
524
|
+
|
|
450
525
|
`SFSymbol` uses **UIKit `UIImageView` + Apple's Symbols framework**, exposed
|
|
451
526
|
through Fabric. It does not embed a SwiftUI hosting view. Xcode 26+ is required
|
|
452
527
|
to build; the deployment target remains iOS 17. Yoga measures the configured
|
|
@@ -559,6 +634,46 @@ multicolor and variable/draw support depend on each symbol's native annotations.
|
|
|
559
634
|
transitions are suppressed while Reduce Motion is enabled, while scale and
|
|
560
635
|
visibility state changes apply instantly. Set it to `false` to opt out.
|
|
561
636
|
|
|
637
|
+
## Example App
|
|
638
|
+
|
|
639
|
+
The [example app](example/src/App.tsx) demonstrates Menu, Popover, TipKit,
|
|
640
|
+
LiquidGlassText, Stepper, ConcentricView, and SFSymbol. It includes all native
|
|
641
|
+
components, so building it requires Xcode 26+. Use iOS 26+ to try the glass effects
|
|
642
|
+
and the latest SF Symbol features.
|
|
643
|
+
|
|
644
|
+
From the repository root, install the dependencies:
|
|
645
|
+
|
|
646
|
+
```sh
|
|
647
|
+
yarn
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
Install the example's Pods:
|
|
651
|
+
|
|
652
|
+
```sh
|
|
653
|
+
cd example/ios && pod install
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
Then run Metro and the iOS app from the repository root in separate terminals:
|
|
657
|
+
|
|
658
|
+
```sh
|
|
659
|
+
yarn example start
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
```sh
|
|
663
|
+
yarn example ios
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
The example uses Metro port `8082`, which is already configured in these scripts.
|
|
667
|
+
See the [development workflow](CONTRIBUTING.md#development-workflow) for more
|
|
668
|
+
details.
|
|
669
|
+
|
|
670
|
+
## Apps Using This Library
|
|
671
|
+
|
|
672
|
+
- [Night Vision - LiDAR Camera](https://apps.apple.com/app/id1668629667)
|
|
673
|
+
- [Laser Measure - LiDAR Powered](https://apps.apple.com/app/id6466744678)
|
|
674
|
+
- [PhoneAway - Digital Detox](https://apps.apple.com/app/id6744548607)
|
|
675
|
+
- [Fatigue Alert - Stay Awake](https://apps.apple.com/app/id6479893638)
|
|
676
|
+
|
|
562
677
|
## Contributing
|
|
563
678
|
|
|
564
679
|
- [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,33 @@ using namespace facebook::react;
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
#pragma mark - Touch handling
|
|
68
|
+
|
|
69
|
+
- (SharedTouchEventEmitter)touchEventEmitterAtPoint:(CGPoint)point
|
|
70
|
+
{
|
|
71
|
+
// The provider's hitTest returns its UIButton, so the surface touch handler
|
|
72
|
+
// finds this Fabric view first. Keep the trigger's touch sequence in UIKit:
|
|
73
|
+
// forwarding it to JS lets an ancestor Pressable/Touchable claim the same
|
|
74
|
+
// touch and run its press callbacks while the native menu is opening.
|
|
75
|
+
if (_menuProvider.isEnabled) {
|
|
76
|
+
return nullptr;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return [super touchEventEmitterAtPoint:point];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#pragma mark - Commands
|
|
83
|
+
|
|
84
|
+
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
|
|
85
|
+
{
|
|
86
|
+
RCTTinyuiMenuViewHandleCommand(self, commandName, args);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
- (void)open
|
|
90
|
+
{
|
|
91
|
+
[_menuProvider openMenu];
|
|
92
|
+
}
|
|
93
|
+
|
|
67
94
|
#pragma mark - Props
|
|
68
95
|
|
|
69
96
|
- (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.1",
|
|
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
|
> {}
|