@tamagui/create-menu 2.0.0-rc.8 → 2.0.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/dist/cjs/MenuPredefined.cjs +159 -157
- package/dist/cjs/MenuPredefined.native.js +159 -157
- package/dist/cjs/MenuPredefined.native.js.map +1 -1
- package/dist/cjs/createBaseMenu.cjs +1150 -941
- package/dist/cjs/createBaseMenu.native.js +1280 -1108
- package/dist/cjs/createBaseMenu.native.js.map +1 -1
- package/dist/cjs/createNativeMenu/createNativeMenu.cjs +318 -159
- package/dist/cjs/createNativeMenu/createNativeMenu.native.js +430 -267
- package/dist/cjs/createNativeMenu/createNativeMenu.native.js.map +1 -1
- package/dist/cjs/createNativeMenu/createNativeMenuTypes.cjs +7 -5
- package/dist/cjs/createNativeMenu/createNativeMenuTypes.native.js +7 -5
- package/dist/cjs/createNativeMenu/createNativeMenuTypes.native.js.map +1 -1
- package/dist/cjs/createNativeMenu/utils.cjs +85 -42
- package/dist/cjs/createNativeMenu/utils.native.js +83 -58
- package/dist/cjs/createNativeMenu/utils.native.js.map +1 -1
- package/dist/cjs/createNativeMenu/withNativeMenu.cjs +27 -17
- package/dist/cjs/createNativeMenu/withNativeMenu.native.js +22 -14
- package/dist/cjs/createNativeMenu/withNativeMenu.native.js.map +1 -1
- package/dist/cjs/index.cjs +15 -12
- package/dist/cjs/index.native.js +15 -12
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/esm/MenuPredefined.mjs +144 -144
- package/dist/esm/MenuPredefined.mjs.map +1 -1
- package/dist/esm/MenuPredefined.native.js +144 -144
- package/dist/esm/MenuPredefined.native.js.map +1 -1
- package/dist/esm/createBaseMenu.mjs +1110 -903
- package/dist/esm/createBaseMenu.mjs.map +1 -1
- package/dist/esm/createBaseMenu.native.js +1240 -1070
- package/dist/esm/createBaseMenu.native.js.map +1 -1
- package/dist/esm/createNativeMenu/createNativeMenu.mjs +291 -134
- package/dist/esm/createNativeMenu/createNativeMenu.mjs.map +1 -1
- package/dist/esm/createNativeMenu/createNativeMenu.native.js +377 -216
- package/dist/esm/createNativeMenu/createNativeMenu.native.js.map +1 -1
- package/dist/esm/createNativeMenu/utils.mjs +58 -17
- package/dist/esm/createNativeMenu/utils.mjs.map +1 -1
- package/dist/esm/createNativeMenu/utils.native.js +57 -34
- package/dist/esm/createNativeMenu/utils.native.js.map +1 -1
- package/dist/esm/createNativeMenu/withNativeMenu.mjs +13 -5
- package/dist/esm/createNativeMenu/withNativeMenu.mjs.map +1 -1
- package/dist/esm/createNativeMenu/withNativeMenu.native.js +8 -2
- package/dist/esm/createNativeMenu/withNativeMenu.native.js.map +1 -1
- package/dist/esm/index.js +5 -6
- package/dist/esm/index.js.map +1 -6
- package/dist/esm/index.mjs +2 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/jsx/MenuPredefined.mjs +144 -144
- package/dist/jsx/MenuPredefined.mjs.map +1 -1
- package/dist/jsx/MenuPredefined.native.js +159 -157
- package/dist/jsx/MenuPredefined.native.js.map +1 -1
- package/dist/jsx/createBaseMenu.mjs +1110 -903
- package/dist/jsx/createBaseMenu.mjs.map +1 -1
- package/dist/jsx/createBaseMenu.native.js +1280 -1108
- package/dist/jsx/createBaseMenu.native.js.map +1 -1
- package/dist/jsx/createNativeMenu/createNativeMenu.mjs +291 -134
- package/dist/jsx/createNativeMenu/createNativeMenu.mjs.map +1 -1
- package/dist/jsx/createNativeMenu/createNativeMenu.native.js +430 -267
- package/dist/jsx/createNativeMenu/createNativeMenu.native.js.map +1 -1
- package/dist/jsx/createNativeMenu/createNativeMenuTypes.native.js +7 -5
- package/dist/jsx/createNativeMenu/utils.mjs +58 -17
- package/dist/jsx/createNativeMenu/utils.mjs.map +1 -1
- package/dist/jsx/createNativeMenu/utils.native.js +83 -58
- package/dist/jsx/createNativeMenu/utils.native.js.map +1 -1
- package/dist/jsx/createNativeMenu/withNativeMenu.mjs +13 -5
- package/dist/jsx/createNativeMenu/withNativeMenu.mjs.map +1 -1
- package/dist/jsx/createNativeMenu/withNativeMenu.native.js +22 -14
- package/dist/jsx/createNativeMenu/withNativeMenu.native.js.map +1 -1
- package/dist/jsx/index.js +5 -6
- package/dist/jsx/index.js.map +1 -6
- package/dist/jsx/index.mjs +2 -1
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +15 -12
- package/dist/jsx/index.native.js.map +1 -1
- package/package.json +26 -29
- package/src/createBaseMenu.tsx +367 -266
- package/src/createNativeMenu/createNativeMenu.tsx +448 -220
- package/src/createNativeMenu/createNativeMenuTypes.ts +20 -20
- package/src/createNativeMenu/withNativeMenu.tsx +5 -3
- package/src/index.tsx +3 -5
- package/types/createBaseMenu.d.ts +117 -31
- package/types/createBaseMenu.d.ts.map +1 -1
- package/types/createNativeMenu/createNativeMenu.d.ts +21 -21
- package/types/createNativeMenu/createNativeMenu.d.ts.map +1 -1
- package/types/createNativeMenu/createNativeMenuTypes.d.ts +20 -20
- package/types/createNativeMenu/createNativeMenuTypes.d.ts.map +1 -1
- package/types/createNativeMenu/withNativeMenu.d.ts +3 -3
- package/types/createNativeMenu/withNativeMenu.d.ts.map +1 -1
- package/types/index.d.ts +3 -2
- package/types/index.d.ts.map +1 -1
- package/dist/cjs/MenuPredefined.js +0 -168
- package/dist/cjs/MenuPredefined.js.map +0 -6
- package/dist/cjs/createBaseMenu.js +0 -843
- package/dist/cjs/createBaseMenu.js.map +0 -6
- package/dist/cjs/createNativeMenu/createNativeMenu.js +0 -177
- package/dist/cjs/createNativeMenu/createNativeMenu.js.map +0 -6
- package/dist/cjs/createNativeMenu/createNativeMenuTypes.js +0 -14
- package/dist/cjs/createNativeMenu/createNativeMenuTypes.js.map +0 -6
- package/dist/cjs/createNativeMenu/index.cjs +0 -19
- package/dist/cjs/createNativeMenu/index.js +0 -16
- package/dist/cjs/createNativeMenu/index.js.map +0 -6
- package/dist/cjs/createNativeMenu/index.native.js +0 -22
- package/dist/cjs/createNativeMenu/index.native.js.map +0 -1
- package/dist/cjs/createNativeMenu/utils.js +0 -66
- package/dist/cjs/createNativeMenu/utils.js.map +0 -6
- package/dist/cjs/createNativeMenu/withNativeMenu.js +0 -30
- package/dist/cjs/createNativeMenu/withNativeMenu.js.map +0 -6
- package/dist/cjs/index.js +0 -23
- package/dist/cjs/index.js.map +0 -6
- package/dist/esm/MenuPredefined.js +0 -154
- package/dist/esm/MenuPredefined.js.map +0 -6
- package/dist/esm/createBaseMenu.js +0 -849
- package/dist/esm/createBaseMenu.js.map +0 -6
- package/dist/esm/createNativeMenu/createNativeMenu.js +0 -156
- package/dist/esm/createNativeMenu/createNativeMenu.js.map +0 -6
- package/dist/esm/createNativeMenu/createNativeMenuTypes.js +0 -1
- package/dist/esm/createNativeMenu/createNativeMenuTypes.js.map +0 -6
- package/dist/esm/createNativeMenu/index.js +0 -3
- package/dist/esm/createNativeMenu/index.js.map +0 -6
- package/dist/esm/createNativeMenu/index.mjs +0 -3
- package/dist/esm/createNativeMenu/index.mjs.map +0 -1
- package/dist/esm/createNativeMenu/index.native.js +0 -3
- package/dist/esm/createNativeMenu/index.native.js.map +0 -1
- package/dist/esm/createNativeMenu/utils.js +0 -47
- package/dist/esm/createNativeMenu/utils.js.map +0 -6
- package/dist/esm/createNativeMenu/withNativeMenu.js +0 -15
- package/dist/esm/createNativeMenu/withNativeMenu.js.map +0 -6
- package/dist/jsx/MenuPredefined.js +0 -154
- package/dist/jsx/MenuPredefined.js.map +0 -6
- package/dist/jsx/createBaseMenu.js +0 -849
- package/dist/jsx/createBaseMenu.js.map +0 -6
- package/dist/jsx/createNativeMenu/createNativeMenu.js +0 -156
- package/dist/jsx/createNativeMenu/createNativeMenu.js.map +0 -6
- package/dist/jsx/createNativeMenu/createNativeMenuTypes.js +0 -1
- package/dist/jsx/createNativeMenu/createNativeMenuTypes.js.map +0 -6
- package/dist/jsx/createNativeMenu/index.js +0 -3
- package/dist/jsx/createNativeMenu/index.js.map +0 -6
- package/dist/jsx/createNativeMenu/index.mjs +0 -3
- package/dist/jsx/createNativeMenu/index.mjs.map +0 -1
- package/dist/jsx/createNativeMenu/index.native.js +0 -22
- package/dist/jsx/createNativeMenu/index.native.js.map +0 -1
- package/dist/jsx/createNativeMenu/utils.js +0 -47
- package/dist/jsx/createNativeMenu/utils.js.map +0 -6
- package/dist/jsx/createNativeMenu/withNativeMenu.js +0 -15
- package/dist/jsx/createNativeMenu/withNativeMenu.js.map +0 -6
- package/src/createNativeMenu/index.tsx +0 -7
- package/types/createNativeMenu/index.d.ts +0 -4
- package/types/createNativeMenu/index.d.ts.map +0 -1
|
@@ -1,194 +1,260 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* createNativeMenu -
|
|
2
|
+
* createNativeMenu - native menu implementation for React Native
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Web: returns empty stub components (withNativeMenu uses the web components instead)
|
|
5
|
+
* Native: lazily resolves Zeego at render time so importing the package doesn't warn/error
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
getZeego,
|
|
10
|
+
NativeMenuContext,
|
|
11
|
+
unstable_claimExternalPressOwnership,
|
|
12
|
+
unstable_releaseExternalPressOwnership,
|
|
13
|
+
} from '@tamagui/native'
|
|
9
14
|
import { isWeb, withStaticProperties, isIos } from '@tamagui/web'
|
|
10
15
|
import type { FC } from 'react'
|
|
11
16
|
import React from 'react'
|
|
12
17
|
import type {
|
|
13
18
|
ContextMenuPreviewProps,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
NativeContextMenuAuxiliaryProps,
|
|
20
|
+
NativeMenuArrowProps,
|
|
21
|
+
NativeMenuCheckboxItemProps,
|
|
22
|
+
NativeMenuContentProps,
|
|
23
|
+
NativeMenuGroupProps,
|
|
24
|
+
NativeMenuItemIconProps,
|
|
25
|
+
NativeMenuItemImageProps,
|
|
26
|
+
NativeMenuItemIndicatorProps,
|
|
27
|
+
NativeMenuItemProps,
|
|
28
|
+
NativeMenuItemSubtitleProps,
|
|
29
|
+
NativeMenuItemTitleProps,
|
|
30
|
+
NativeMenuLabelProps,
|
|
31
|
+
NativeMenuProps,
|
|
32
|
+
NativeMenuSeparatorProps,
|
|
33
|
+
NativeMenuSubContentProps,
|
|
34
|
+
NativeMenuSubProps,
|
|
35
|
+
NativeMenuSubTriggerProps,
|
|
30
36
|
MenuTriggerProps,
|
|
31
37
|
} from './createNativeMenuTypes'
|
|
32
38
|
|
|
39
|
+
// zeego module shape (DropdownMenu / ContextMenu both share this)
|
|
40
|
+
type ZeegoMenuModule = {
|
|
41
|
+
Root: FC<Record<string, unknown>>
|
|
42
|
+
Trigger: FC<MenuTriggerProps>
|
|
43
|
+
Content: FC<NativeMenuContentProps>
|
|
44
|
+
Item: FC<NativeMenuItemProps>
|
|
45
|
+
ItemTitle: FC<NativeMenuItemTitleProps>
|
|
46
|
+
ItemSubtitle: FC<NativeMenuItemSubtitleProps>
|
|
47
|
+
ItemIcon: FC<NativeMenuItemIconProps>
|
|
48
|
+
ItemImage: FC<NativeMenuItemImageProps>
|
|
49
|
+
ItemIndicator: FC<NativeMenuItemIndicatorProps>
|
|
50
|
+
Group: FC<NativeMenuGroupProps>
|
|
51
|
+
Label: FC<NativeMenuLabelProps>
|
|
52
|
+
Separator: FC<NativeMenuSeparatorProps>
|
|
53
|
+
Sub: FC<NativeMenuSubProps>
|
|
54
|
+
SubTrigger: FC<NativeMenuSubTriggerProps>
|
|
55
|
+
SubContent: FC<NativeMenuSubContentProps>
|
|
56
|
+
CheckboxItem: FC<NativeMenuCheckboxItemProps>
|
|
57
|
+
Preview: FC<ContextMenuPreviewProps>
|
|
58
|
+
Auxiliary: FC<NativeContextMenuAuxiliaryProps>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// component types we recognize via displayName matching
|
|
62
|
+
type MappedComponentType =
|
|
63
|
+
| 'SubContent'
|
|
64
|
+
| 'SubTrigger'
|
|
65
|
+
| 'Content'
|
|
66
|
+
| 'Sub'
|
|
67
|
+
| 'Group'
|
|
68
|
+
| 'CheckboxItem'
|
|
69
|
+
|
|
70
|
+
const MAPPED_TYPES: MappedComponentType[] = [
|
|
71
|
+
'SubContent',
|
|
72
|
+
'SubTrigger',
|
|
73
|
+
'Content',
|
|
74
|
+
'Sub',
|
|
75
|
+
'Group',
|
|
76
|
+
'CheckboxItem',
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
// types whose children get recursively transformed
|
|
80
|
+
const CONTAINER_TYPES: MappedComponentType[] = ['SubContent', 'Content', 'Sub', 'Group']
|
|
81
|
+
|
|
82
|
+
type ComponentMap = Pick<
|
|
83
|
+
ZeegoMenuModule,
|
|
84
|
+
'SubContent' | 'Content' | 'Sub' | 'Group' | 'SubTrigger'
|
|
85
|
+
>
|
|
86
|
+
|
|
87
|
+
type TriggerPressBoundaryHandlers = {
|
|
88
|
+
claim(debugName?: string | null): void
|
|
89
|
+
release(debugName?: string | null): void
|
|
90
|
+
}
|
|
91
|
+
|
|
33
92
|
export type NativeMenuComponents = {
|
|
34
|
-
Menu: FC<
|
|
93
|
+
Menu: FC<NativeMenuProps> & {
|
|
35
94
|
Trigger: FC<MenuTriggerProps>
|
|
36
|
-
Content: FC<
|
|
37
|
-
Item: FC<
|
|
38
|
-
ItemTitle: FC<
|
|
39
|
-
ItemSubtitle: FC<
|
|
40
|
-
SubTrigger: FC<
|
|
41
|
-
Group: FC<
|
|
42
|
-
ItemIcon: FC<
|
|
43
|
-
Separator: FC<
|
|
44
|
-
CheckboxItem: FC<
|
|
45
|
-
ItemIndicator: FC<
|
|
46
|
-
ItemImage: FC<
|
|
47
|
-
Label: FC<
|
|
48
|
-
Arrow: FC<
|
|
49
|
-
Sub: FC<
|
|
50
|
-
SubContent: FC<
|
|
95
|
+
Content: FC<NativeMenuContentProps>
|
|
96
|
+
Item: FC<NativeMenuItemProps>
|
|
97
|
+
ItemTitle: FC<NativeMenuItemTitleProps>
|
|
98
|
+
ItemSubtitle: FC<NativeMenuItemSubtitleProps>
|
|
99
|
+
SubTrigger: FC<NativeMenuSubTriggerProps>
|
|
100
|
+
Group: FC<NativeMenuGroupProps>
|
|
101
|
+
ItemIcon: FC<NativeMenuItemIconProps>
|
|
102
|
+
Separator: FC<NativeMenuSeparatorProps>
|
|
103
|
+
CheckboxItem: FC<NativeMenuCheckboxItemProps>
|
|
104
|
+
ItemIndicator: FC<NativeMenuItemIndicatorProps>
|
|
105
|
+
ItemImage: FC<NativeMenuItemImageProps>
|
|
106
|
+
Label: FC<NativeMenuLabelProps>
|
|
107
|
+
Arrow: FC<NativeMenuArrowProps>
|
|
108
|
+
Sub: FC<NativeMenuSubProps>
|
|
109
|
+
SubContent: FC<NativeMenuSubContentProps>
|
|
51
110
|
Preview: FC<ContextMenuPreviewProps>
|
|
52
111
|
Portal: FC<{ children: React.ReactNode }>
|
|
53
112
|
RadioGroup: FC<{ children: React.ReactNode }>
|
|
54
113
|
RadioItem: FC<{ children: React.ReactNode }>
|
|
55
|
-
Auxiliary: FC<
|
|
114
|
+
Auxiliary: FC<NativeContextMenuAuxiliaryProps>
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// shared helpers (stateless, no need to recreate per call)
|
|
119
|
+
|
|
120
|
+
function getComponentType(displayName: string): MappedComponentType | null {
|
|
121
|
+
for (const type of MAPPED_TYPES) {
|
|
122
|
+
if (displayName === type || displayName.includes(`(${type})`)) {
|
|
123
|
+
return type
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function isItemLike(props: Record<string, unknown>, displayName: string): boolean {
|
|
130
|
+
if (getComponentType(displayName)) return false
|
|
131
|
+
return 'onSelect' in props || 'textValue' in props
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function isPortalLike(displayName: string): boolean {
|
|
135
|
+
return displayName === 'Portal' || displayName.includes('Portal')
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isTriggerLike(displayName: string): boolean {
|
|
139
|
+
return displayName === 'Trigger' || displayName.includes('(Trigger)')
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function composeHandlers<T extends (...args: any[]) => void>(first?: T, second?: T) {
|
|
143
|
+
return (...args: Parameters<T>) => {
|
|
144
|
+
first?.(...args)
|
|
145
|
+
second?.(...args)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getTriggerDebugName(
|
|
150
|
+
menuType: 'ContextMenu' | 'Menu',
|
|
151
|
+
props: Record<string, any>
|
|
152
|
+
) {
|
|
153
|
+
const childProps =
|
|
154
|
+
React.isValidElement(props.children) && props.children.props
|
|
155
|
+
? (props.children.props as Record<string, any>)
|
|
156
|
+
: null
|
|
157
|
+
|
|
158
|
+
const prefix = menuType === 'ContextMenu' ? 'ContextMenuTrigger' : 'MenuTrigger'
|
|
159
|
+
const detail =
|
|
160
|
+
childProps?.testID ??
|
|
161
|
+
childProps?.accessibilityLabel ??
|
|
162
|
+
(typeof props.textValue === 'string' ? props.textValue : null)
|
|
163
|
+
|
|
164
|
+
return [prefix, detail].filter(Boolean).join(':') || prefix
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// stub used for web — never actually rendered, just needs to exist for withNativeMenu fallback
|
|
168
|
+
const emptyStub = (() => null) as FC<any>
|
|
169
|
+
|
|
170
|
+
function createWebStubs(): NativeMenuComponents {
|
|
171
|
+
return {
|
|
172
|
+
Menu: withStaticProperties(emptyStub as FC<NativeMenuProps>, {
|
|
173
|
+
Trigger: emptyStub as FC<MenuTriggerProps>,
|
|
174
|
+
Content: emptyStub as FC<NativeMenuContentProps>,
|
|
175
|
+
Item: emptyStub as FC<NativeMenuItemProps>,
|
|
176
|
+
ItemTitle: emptyStub as FC<NativeMenuItemTitleProps>,
|
|
177
|
+
ItemSubtitle: emptyStub as FC<NativeMenuItemSubtitleProps>,
|
|
178
|
+
SubTrigger: emptyStub as FC<NativeMenuSubTriggerProps>,
|
|
179
|
+
Group: emptyStub as FC<NativeMenuGroupProps>,
|
|
180
|
+
ItemIcon: emptyStub as FC<NativeMenuItemIconProps>,
|
|
181
|
+
Separator: emptyStub as FC<NativeMenuSeparatorProps>,
|
|
182
|
+
CheckboxItem: emptyStub as FC<NativeMenuCheckboxItemProps>,
|
|
183
|
+
ItemIndicator: emptyStub as FC<NativeMenuItemIndicatorProps>,
|
|
184
|
+
ItemImage: emptyStub as FC<NativeMenuItemImageProps>,
|
|
185
|
+
Label: emptyStub as FC<NativeMenuLabelProps>,
|
|
186
|
+
Arrow: emptyStub as FC<NativeMenuArrowProps>,
|
|
187
|
+
Sub: emptyStub as FC<NativeMenuSubProps>,
|
|
188
|
+
SubContent: emptyStub as FC<NativeMenuSubContentProps>,
|
|
189
|
+
Preview: emptyStub as FC<ContextMenuPreviewProps>,
|
|
190
|
+
Portal: emptyStub as FC<{ children: React.ReactNode }>,
|
|
191
|
+
RadioGroup: emptyStub as FC<{ children: React.ReactNode }>,
|
|
192
|
+
RadioItem: emptyStub as FC<{ children: React.ReactNode }>,
|
|
193
|
+
Auxiliary: emptyStub as FC<NativeContextMenuAuxiliaryProps>,
|
|
194
|
+
}),
|
|
56
195
|
}
|
|
57
196
|
}
|
|
58
197
|
|
|
59
198
|
export const createNativeMenu = (
|
|
60
199
|
MenuType: 'ContextMenu' | 'Menu'
|
|
61
200
|
): NativeMenuComponents => {
|
|
62
|
-
// On web, return empty stubs - withNativeMenu will use the web components passed to it
|
|
63
201
|
if (isWeb) {
|
|
64
|
-
|
|
65
|
-
const Trigger = {} as FC<MenuTriggerProps>
|
|
66
|
-
const Content = {} as FC<MenuContentProps>
|
|
67
|
-
const Preview = {} as FC<ContextMenuPreviewProps>
|
|
68
|
-
const Item = {} as FC<MenuItemProps>
|
|
69
|
-
const ItemIcon = {} as FC<MenuItemIconProps>
|
|
70
|
-
const ItemImage = {} as FC<MenuItemImageProps>
|
|
71
|
-
const SubTrigger = {} as FC<MenuSubTriggerProps>
|
|
72
|
-
const ItemTitle = {} as FC<MenuItemTitleProps>
|
|
73
|
-
const ItemSubtitle = {} as FC<MenuItemSubtitleProps>
|
|
74
|
-
const Group = {} as FC<MenuGroupProps>
|
|
75
|
-
const Separator = {} as FC<MenuSeparatorProps>
|
|
76
|
-
const CheckboxItem = {} as FC<MenuCheckboxItemProps>
|
|
77
|
-
const ItemIndicator = {} as FC<MenuItemIndicatorProps>
|
|
78
|
-
const Label = {} as FC<MenuLabelProps>
|
|
79
|
-
const Arrow = {} as FC<MenuArrowProps>
|
|
80
|
-
const Sub = {} as FC<MenuSubProps>
|
|
81
|
-
const SubContent = {} as FC<MenuSubContentProps>
|
|
82
|
-
const Portal = {} as FC<{ children: React.ReactNode }>
|
|
83
|
-
const RadioGroup = {} as FC<{ children: React.ReactNode }>
|
|
84
|
-
const RadioItem = {} as FC<{ children: React.ReactNode }>
|
|
85
|
-
const Auxiliary = {} as FC<any>
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
Menu: withStaticProperties(Menu, {
|
|
89
|
-
Trigger,
|
|
90
|
-
Content,
|
|
91
|
-
Item,
|
|
92
|
-
ItemTitle,
|
|
93
|
-
ItemSubtitle,
|
|
94
|
-
SubTrigger,
|
|
95
|
-
Group,
|
|
96
|
-
ItemIcon,
|
|
97
|
-
Separator,
|
|
98
|
-
CheckboxItem,
|
|
99
|
-
ItemIndicator,
|
|
100
|
-
ItemImage,
|
|
101
|
-
Label,
|
|
102
|
-
Arrow,
|
|
103
|
-
Sub,
|
|
104
|
-
SubContent,
|
|
105
|
-
Preview,
|
|
106
|
-
Portal,
|
|
107
|
-
RadioGroup,
|
|
108
|
-
RadioItem,
|
|
109
|
-
Auxiliary,
|
|
110
|
-
}),
|
|
111
|
-
}
|
|
202
|
+
return createWebStubs()
|
|
112
203
|
}
|
|
113
204
|
|
|
114
205
|
// ===========================================
|
|
115
|
-
//
|
|
206
|
+
// native implementation — lazily resolves zeego
|
|
116
207
|
// ===========================================
|
|
117
208
|
|
|
118
|
-
const zeego = getZeego()
|
|
119
|
-
if (!zeego.isEnabled) {
|
|
120
|
-
console.warn(
|
|
121
|
-
`Warning: Must call import '@tamagui/native/setup-zeego' at your app entry point to use native menus`
|
|
122
|
-
)
|
|
123
|
-
return { Menu: {} as any }
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const { DropdownMenu: ZeegoDropdownMenu, ContextMenu: ZeegoContextMenu } = zeego.state
|
|
127
|
-
|
|
128
209
|
const isContextMenu = MenuType === 'ContextMenu'
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
* Get component type from displayName (handles styled wrappers)
|
|
145
|
-
*/
|
|
146
|
-
const getComponentType = (displayName: string): string | null => {
|
|
147
|
-
// Check in specific order (SubContent before Content, SubTrigger before Trigger)
|
|
148
|
-
for (const type of [
|
|
149
|
-
'SubContent',
|
|
150
|
-
'SubTrigger',
|
|
151
|
-
'Content',
|
|
152
|
-
'Sub',
|
|
153
|
-
'Group',
|
|
154
|
-
'CheckboxItem',
|
|
155
|
-
]) {
|
|
156
|
-
if (displayName === type || displayName.includes(`(${type})`)) {
|
|
157
|
-
return type
|
|
210
|
+
const isAndroid = !isIos && !isWeb
|
|
211
|
+
|
|
212
|
+
// cached after first successful resolve
|
|
213
|
+
let resolved: { menu: ZeegoMenuModule; componentMap: ComponentMap } | null = null
|
|
214
|
+
let warned = false
|
|
215
|
+
|
|
216
|
+
function resolve(): typeof resolved {
|
|
217
|
+
if (resolved) return resolved
|
|
218
|
+
const zeego = getZeego()
|
|
219
|
+
if (!zeego.isEnabled) {
|
|
220
|
+
if (!warned) {
|
|
221
|
+
warned = true
|
|
222
|
+
console.warn(
|
|
223
|
+
`Warning: Must call import '@tamagui/native/setup-zeego' at your app entry point to use native menus`
|
|
224
|
+
)
|
|
158
225
|
}
|
|
226
|
+
return null
|
|
159
227
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
228
|
+
const menu = (
|
|
229
|
+
isContextMenu ? zeego.state.ContextMenu : zeego.state.DropdownMenu
|
|
230
|
+
) as ZeegoMenuModule
|
|
231
|
+
resolved = {
|
|
232
|
+
menu,
|
|
233
|
+
componentMap: {
|
|
234
|
+
SubContent: menu.SubContent,
|
|
235
|
+
Content: menu.Content,
|
|
236
|
+
Sub: menu.Sub,
|
|
237
|
+
Group: menu.Group,
|
|
238
|
+
SubTrigger: menu.SubTrigger,
|
|
239
|
+
},
|
|
170
240
|
}
|
|
171
|
-
return
|
|
241
|
+
return resolved
|
|
172
242
|
}
|
|
173
243
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const isPortal = (displayName: string): boolean => {
|
|
178
|
-
return displayName === 'Portal' || displayName.includes('Portal')
|
|
244
|
+
type RadioContext = {
|
|
245
|
+
value?: string
|
|
246
|
+
onValueChange?: (value: string) => void
|
|
179
247
|
}
|
|
180
248
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
* - Convert styled Items to Zeego Items
|
|
186
|
-
* - Reverse children on iOS only for DropdownMenu at Content/SubContent level
|
|
187
|
-
*/
|
|
188
|
-
const transformForZeego = (
|
|
249
|
+
// transform children tree for zeego compatibility
|
|
250
|
+
function transformChildren(
|
|
251
|
+
menu: ZeegoMenuModule,
|
|
252
|
+
map: ComponentMap,
|
|
189
253
|
children: React.ReactNode,
|
|
190
|
-
shouldReverseOnIos = false
|
|
191
|
-
|
|
254
|
+
shouldReverseOnIos = false,
|
|
255
|
+
triggerBoundaryHandlers?: TriggerPressBoundaryHandlers,
|
|
256
|
+
radioContext?: RadioContext
|
|
257
|
+
): React.ReactNode {
|
|
192
258
|
const result: React.ReactNode[] = []
|
|
193
259
|
|
|
194
260
|
React.Children.forEach(children, (child) => {
|
|
@@ -197,49 +263,149 @@ export const createNativeMenu = (
|
|
|
197
263
|
return
|
|
198
264
|
}
|
|
199
265
|
|
|
200
|
-
const displayName = (child.type as
|
|
266
|
+
const displayName = (child.type as { displayName?: string })?.displayName || ''
|
|
201
267
|
const props = child.props as Record<string, any>
|
|
202
268
|
|
|
203
|
-
//
|
|
204
|
-
if (
|
|
205
|
-
const
|
|
206
|
-
|
|
269
|
+
// flatten portal wrappers
|
|
270
|
+
if (isPortalLike(displayName)) {
|
|
271
|
+
const inner = transformChildren(
|
|
272
|
+
menu,
|
|
273
|
+
map,
|
|
274
|
+
props.children as React.ReactNode,
|
|
275
|
+
false,
|
|
276
|
+
triggerBoundaryHandlers,
|
|
277
|
+
radioContext
|
|
278
|
+
)
|
|
279
|
+
React.Children.forEach(inner, (c) => result.push(c))
|
|
280
|
+
return
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// flatten ScrollView (native passthrough — children need to be visible to zeego)
|
|
284
|
+
if (displayName.includes('ScrollView')) {
|
|
285
|
+
const inner = transformChildren(
|
|
286
|
+
menu,
|
|
287
|
+
map,
|
|
288
|
+
props.children as React.ReactNode,
|
|
289
|
+
false,
|
|
290
|
+
triggerBoundaryHandlers,
|
|
291
|
+
radioContext
|
|
292
|
+
)
|
|
293
|
+
React.Children.forEach(inner, (c) => result.push(c))
|
|
294
|
+
return
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (isTriggerLike(displayName)) {
|
|
298
|
+
const debugName = getTriggerDebugName(MenuType, props)
|
|
299
|
+
const claim = () => triggerBoundaryHandlers?.claim(debugName)
|
|
300
|
+
const release = () => triggerBoundaryHandlers?.release(debugName)
|
|
301
|
+
|
|
302
|
+
result.push(
|
|
303
|
+
React.cloneElement(child, {
|
|
304
|
+
onTouchStart: composeHandlers(claim, props.onTouchStart),
|
|
305
|
+
onTouchEnd: composeHandlers(props.onTouchEnd, release),
|
|
306
|
+
onTouchCancel: composeHandlers(props.onTouchCancel, release),
|
|
307
|
+
onResponderGrant: composeHandlers(claim, props.onResponderGrant),
|
|
308
|
+
onResponderRelease: composeHandlers(props.onResponderRelease, release),
|
|
309
|
+
onResponderTerminate: composeHandlers(props.onResponderTerminate, release),
|
|
310
|
+
onPressIn: composeHandlers(claim, props.onPressIn),
|
|
311
|
+
onPressOut: composeHandlers(props.onPressOut, release),
|
|
312
|
+
} as any)
|
|
313
|
+
)
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// RadioGroup: render as a zeego Group and pipe value/onValueChange
|
|
318
|
+
// down to any RadioItem descendants via radioContext
|
|
319
|
+
if (displayName.includes('RadioGroup')) {
|
|
320
|
+
const {
|
|
321
|
+
value: rgValue,
|
|
322
|
+
onValueChange: rgOnValueChange,
|
|
323
|
+
children: rgChildren,
|
|
324
|
+
...rest
|
|
325
|
+
} = props as Record<string, any>
|
|
326
|
+
|
|
327
|
+
result.push(
|
|
328
|
+
React.createElement(
|
|
329
|
+
menu.Group,
|
|
330
|
+
{ ...rest, key: child.key } as any,
|
|
331
|
+
transformChildren(
|
|
332
|
+
menu,
|
|
333
|
+
map,
|
|
334
|
+
rgChildren as React.ReactNode,
|
|
335
|
+
false,
|
|
336
|
+
triggerBoundaryHandlers,
|
|
337
|
+
{ value: rgValue, onValueChange: rgOnValueChange }
|
|
338
|
+
)
|
|
339
|
+
)
|
|
340
|
+
)
|
|
341
|
+
return
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// RadioItem: zeego has no radio primitive, so emit a CheckboxItem whose
|
|
345
|
+
// 'on'/'off' state is derived from the enclosing RadioGroup's value.
|
|
346
|
+
if (displayName.includes('RadioItem') && radioContext) {
|
|
347
|
+
const {
|
|
348
|
+
value: itemValue,
|
|
349
|
+
children: rChildren,
|
|
350
|
+
...rest
|
|
351
|
+
} = props as Record<string, any>
|
|
352
|
+
|
|
353
|
+
const cleanChildren = React.Children.map(rChildren, (c) => {
|
|
354
|
+
if (!React.isValidElement(c)) return c
|
|
355
|
+
const dn = (c.type as { displayName?: string })?.displayName || ''
|
|
356
|
+
if (dn.includes('ItemIndicator')) return null
|
|
357
|
+
return c
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
result.push(
|
|
361
|
+
React.createElement(
|
|
362
|
+
menu.CheckboxItem,
|
|
363
|
+
{
|
|
364
|
+
...rest,
|
|
365
|
+
key: child.key,
|
|
366
|
+
value: itemValue === radioContext.value ? 'on' : 'off',
|
|
367
|
+
onValueChange: () => radioContext.onValueChange?.(itemValue),
|
|
368
|
+
} as any,
|
|
369
|
+
cleanChildren
|
|
370
|
+
)
|
|
371
|
+
)
|
|
207
372
|
return
|
|
208
373
|
}
|
|
209
374
|
|
|
210
|
-
// Handle known component types (containers, SubTrigger, CheckboxItem)
|
|
211
375
|
const componentType = getComponentType(displayName)
|
|
212
376
|
|
|
213
|
-
//
|
|
377
|
+
// normalize checkbox checked/value props
|
|
214
378
|
if (componentType === 'CheckboxItem') {
|
|
215
|
-
const {
|
|
216
|
-
|
|
379
|
+
const {
|
|
380
|
+
checked,
|
|
381
|
+
onCheckedChange,
|
|
382
|
+
value,
|
|
383
|
+
onValueChange,
|
|
384
|
+
children: cbChildren,
|
|
385
|
+
...rest
|
|
386
|
+
} = props as Record<string, any>
|
|
217
387
|
|
|
218
388
|
const finalValue = value ?? (checked ? 'on' : 'off')
|
|
219
389
|
const finalOnValueChange =
|
|
220
390
|
onValueChange ??
|
|
221
391
|
(onCheckedChange && ((v: string) => onCheckedChange(v === 'on')))
|
|
222
392
|
|
|
223
|
-
const cleanChildren = React.Children.map(
|
|
224
|
-
if (!React.isValidElement(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (childDisplayName.includes('ItemIndicator')) {
|
|
229
|
-
return null
|
|
230
|
-
}
|
|
231
|
-
return child
|
|
393
|
+
const cleanChildren = React.Children.map(cbChildren, (c) => {
|
|
394
|
+
if (!React.isValidElement(c)) return c
|
|
395
|
+
const dn = (c.type as { displayName?: string })?.displayName || ''
|
|
396
|
+
if (dn.includes('ItemIndicator')) return null
|
|
397
|
+
return c
|
|
232
398
|
})
|
|
233
399
|
|
|
234
400
|
result.push(
|
|
235
401
|
React.createElement(
|
|
236
|
-
|
|
402
|
+
menu.CheckboxItem,
|
|
237
403
|
{
|
|
238
404
|
...rest,
|
|
239
405
|
key: child.key,
|
|
240
406
|
value: finalValue,
|
|
241
407
|
onValueChange: finalOnValueChange,
|
|
242
|
-
},
|
|
408
|
+
} as any,
|
|
243
409
|
cleanChildren
|
|
244
410
|
)
|
|
245
411
|
)
|
|
@@ -248,41 +414,45 @@ export const createNativeMenu = (
|
|
|
248
414
|
|
|
249
415
|
if (componentType) {
|
|
250
416
|
const { children: childChildren, ...restProps } = props
|
|
251
|
-
const isContainer = CONTAINER_TYPES.includes(componentType)
|
|
252
|
-
|
|
253
|
-
const shouldReverseChildren =
|
|
417
|
+
const isContainer = (CONTAINER_TYPES as string[]).includes(componentType)
|
|
418
|
+
const shouldReverse =
|
|
254
419
|
componentType === 'Content' || componentType === 'SubContent'
|
|
255
420
|
result.push(
|
|
256
421
|
React.createElement(
|
|
257
|
-
|
|
258
|
-
{ ...restProps, key: child.key },
|
|
422
|
+
map[componentType as keyof ComponentMap],
|
|
423
|
+
{ ...restProps, key: child.key } as any,
|
|
259
424
|
isContainer
|
|
260
|
-
?
|
|
425
|
+
? transformChildren(
|
|
426
|
+
menu,
|
|
427
|
+
map,
|
|
428
|
+
childChildren as React.ReactNode,
|
|
429
|
+
shouldReverse,
|
|
430
|
+
triggerBoundaryHandlers,
|
|
431
|
+
radioContext
|
|
432
|
+
)
|
|
261
433
|
: childChildren
|
|
262
434
|
)
|
|
263
435
|
)
|
|
264
436
|
return
|
|
265
437
|
}
|
|
266
438
|
|
|
267
|
-
//
|
|
439
|
+
// convert Item-like components to zeego Items
|
|
268
440
|
if (isItemLike(props, displayName)) {
|
|
269
441
|
const { children: itemChildren, ...itemProps } = props
|
|
270
442
|
result.push(
|
|
271
443
|
React.createElement(
|
|
272
|
-
|
|
273
|
-
{ ...itemProps, key: child.key },
|
|
444
|
+
menu.Item,
|
|
445
|
+
{ ...itemProps, key: child.key } as any,
|
|
274
446
|
itemChildren
|
|
275
447
|
)
|
|
276
448
|
)
|
|
277
449
|
return
|
|
278
450
|
}
|
|
279
451
|
|
|
280
|
-
// Pass through everything else
|
|
281
452
|
result.push(child)
|
|
282
453
|
})
|
|
283
454
|
|
|
284
|
-
// iOS DropdownMenu
|
|
285
|
-
// Only reverse for Menu component, not ContextMenu
|
|
455
|
+
// iOS DropdownMenu displays items in reverse order
|
|
286
456
|
if (isIos && shouldReverseOnIos && !isContextMenu) {
|
|
287
457
|
result.reverse()
|
|
288
458
|
}
|
|
@@ -290,31 +460,39 @@ export const createNativeMenu = (
|
|
|
290
460
|
return result
|
|
291
461
|
}
|
|
292
462
|
|
|
293
|
-
//
|
|
294
|
-
|
|
295
|
-
|
|
463
|
+
// lazy wrapper — resolves the zeego component on first render
|
|
464
|
+
function lazyZeego<P extends Record<string, any>>(
|
|
465
|
+
name: keyof ZeegoMenuModule,
|
|
466
|
+
displayName?: string
|
|
467
|
+
): FC<P> {
|
|
468
|
+
const Comp: FC<P> = (props) => {
|
|
469
|
+
const z = resolve()
|
|
470
|
+
if (!z) return null
|
|
471
|
+
return React.createElement(z.menu[name] as FC<any>, props)
|
|
472
|
+
}
|
|
473
|
+
Comp.displayName = displayName || name
|
|
474
|
+
return Comp
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const Trigger = lazyZeego<MenuTriggerProps>('Trigger')
|
|
478
|
+
const Content = lazyZeego<NativeMenuContentProps>('Content')
|
|
479
|
+
const Item = lazyZeego<NativeMenuItemProps>('Item')
|
|
480
|
+
const ItemTitle = lazyZeego<NativeMenuItemTitleProps>('ItemTitle')
|
|
481
|
+
const ItemSubtitle = lazyZeego<NativeMenuItemSubtitleProps>('ItemSubtitle')
|
|
482
|
+
const ItemIcon = lazyZeego<NativeMenuItemIconProps>('ItemIcon')
|
|
483
|
+
const ItemImage = lazyZeego<NativeMenuItemImageProps>('ItemImage')
|
|
484
|
+
const ItemIndicator = lazyZeego<NativeMenuItemIndicatorProps>('ItemIndicator')
|
|
485
|
+
const Group = lazyZeego<NativeMenuGroupProps>('Group')
|
|
486
|
+
const Label = lazyZeego<NativeMenuLabelProps>('Label')
|
|
487
|
+
const Separator = lazyZeego<NativeMenuSeparatorProps>('Separator')
|
|
488
|
+
const Sub = lazyZeego<NativeMenuSubProps>('Sub')
|
|
489
|
+
const SubTrigger = lazyZeego<NativeMenuSubTriggerProps>('SubTrigger')
|
|
490
|
+
const SubContent = lazyZeego<NativeMenuSubContentProps>('SubContent')
|
|
296
491
|
|
|
297
|
-
// Direct Zeego pass-throughs with proper types
|
|
298
|
-
const Trigger: FC<MenuTriggerProps> = ZeegoMenu.Trigger
|
|
299
|
-
const Content: FC<MenuContentProps> = ZeegoMenu.Content
|
|
300
|
-
const Item: FC<MenuItemProps> = ZeegoMenu.Item
|
|
301
|
-
const ItemTitle: FC<MenuItemTitleProps> = ZeegoMenu.ItemTitle
|
|
302
|
-
const ItemSubtitle: FC<MenuItemSubtitleProps> = ZeegoMenu.ItemSubtitle
|
|
303
|
-
const ItemIcon: FC<MenuItemIconProps> = ZeegoMenu.ItemIcon
|
|
304
|
-
const ItemImage: FC<MenuItemImageProps> = ZeegoMenu.ItemImage
|
|
305
|
-
const ItemIndicator: FC<MenuItemIndicatorProps> = ZeegoMenu.ItemIndicator
|
|
306
|
-
const Group: FC<MenuGroupProps> = ZeegoMenu.Group
|
|
307
|
-
const Label: FC<MenuLabelProps> = ZeegoMenu.Label
|
|
308
|
-
const Separator: FC<MenuSeparatorProps> = ZeegoMenu.Separator
|
|
309
|
-
const Sub: FC<MenuSubProps> = ZeegoMenu.Sub
|
|
310
|
-
const SubTrigger: FC<MenuSubTriggerProps> = ZeegoMenu.SubTrigger
|
|
311
|
-
const SubContent: FC<MenuSubContentProps> = ZeegoMenu.SubContent
|
|
312
|
-
|
|
313
|
-
// Custom components
|
|
314
492
|
const Portal: FC<{ children: React.ReactNode }> = ({ children }) => <>{children}</>
|
|
315
493
|
Portal.displayName = 'Portal'
|
|
316
494
|
|
|
317
|
-
const Arrow: FC<
|
|
495
|
+
const Arrow: FC<NativeMenuArrowProps> = () => null
|
|
318
496
|
Arrow.displayName = 'Arrow'
|
|
319
497
|
|
|
320
498
|
const RadioGroup: FC<{ children: React.ReactNode }> = ({ children }) => <>{children}</>
|
|
@@ -322,36 +500,86 @@ export const createNativeMenu = (
|
|
|
322
500
|
|
|
323
501
|
const RadioItem: FC<{ children: React.ReactNode }> = ({ children }) => <>{children}</>
|
|
324
502
|
RadioItem.displayName = `${MenuType}RadioItem`
|
|
325
|
-
|
|
326
|
-
const CheckboxItem: FC<
|
|
503
|
+
|
|
504
|
+
const CheckboxItem: FC<NativeMenuCheckboxItemProps> = () => null
|
|
327
505
|
CheckboxItem.displayName = 'CheckboxItem'
|
|
328
506
|
|
|
329
|
-
// Context menu specific
|
|
330
507
|
const Preview: FC<ContextMenuPreviewProps> = isContextMenu
|
|
331
|
-
?
|
|
508
|
+
? lazyZeego<ContextMenuPreviewProps>('Preview', `${MenuType}Preview`)
|
|
332
509
|
: () => null
|
|
333
510
|
Preview.displayName = `${MenuType}Preview`
|
|
334
511
|
|
|
335
|
-
const Auxiliary: FC<
|
|
336
|
-
?
|
|
512
|
+
const Auxiliary: FC<NativeContextMenuAuxiliaryProps> = isContextMenu
|
|
513
|
+
? lazyZeego<NativeContextMenuAuxiliaryProps>('Auxiliary', `${MenuType}Auxiliary`)
|
|
337
514
|
: () => null
|
|
338
515
|
Auxiliary.displayName = `${MenuType}Auxiliary`
|
|
339
516
|
|
|
340
|
-
//
|
|
341
|
-
|
|
342
|
-
|
|
517
|
+
// on Android, provide NativeMenuContext so components use Gesture.Manual()
|
|
518
|
+
// instead of Gesture.Tap() (which sends ACTION_CANCEL to MenuView)
|
|
519
|
+
const Menu: FC<NativeMenuProps> = ({ children, onOpenChange, onOpenWillChange }) => {
|
|
520
|
+
const triggerOwnerRef = React.useRef<object | null>(null)
|
|
521
|
+
const claimTriggerBoundary = React.useCallback((debugName?: string | null) => {
|
|
522
|
+
if (triggerOwnerRef.current) {
|
|
523
|
+
unstable_releaseExternalPressOwnership(triggerOwnerRef.current, debugName)
|
|
524
|
+
}
|
|
525
|
+
triggerOwnerRef.current = unstable_claimExternalPressOwnership(debugName)
|
|
526
|
+
}, [])
|
|
527
|
+
|
|
528
|
+
const releaseTriggerBoundary = React.useCallback((debugName?: string | null) => {
|
|
529
|
+
if (!triggerOwnerRef.current) return
|
|
530
|
+
unstable_releaseExternalPressOwnership(triggerOwnerRef.current, debugName)
|
|
531
|
+
triggerOwnerRef.current = null
|
|
532
|
+
}, [])
|
|
533
|
+
|
|
534
|
+
React.useEffect(() => releaseTriggerBoundary, [releaseTriggerBoundary])
|
|
535
|
+
|
|
536
|
+
const z = resolve()
|
|
537
|
+
if (!z) return null
|
|
538
|
+
|
|
539
|
+
const handleOpenChange = React.useCallback(
|
|
540
|
+
(isOpen: boolean) => {
|
|
541
|
+
if (!isOpen) {
|
|
542
|
+
releaseTriggerBoundary()
|
|
543
|
+
}
|
|
544
|
+
onOpenChange?.(isOpen)
|
|
545
|
+
},
|
|
546
|
+
[onOpenChange, releaseTriggerBoundary]
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
const handleOpenWillChange = React.useCallback(
|
|
550
|
+
(willOpen: boolean) => {
|
|
551
|
+
if (!willOpen) {
|
|
552
|
+
releaseTriggerBoundary()
|
|
553
|
+
}
|
|
554
|
+
onOpenWillChange?.(willOpen)
|
|
555
|
+
},
|
|
556
|
+
[onOpenWillChange, releaseTriggerBoundary]
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
const rootProps: Record<string, unknown> = { onOpenChange: handleOpenChange }
|
|
343
560
|
if (isContextMenu && onOpenWillChange) {
|
|
344
|
-
rootProps.onOpenWillChange =
|
|
561
|
+
rootProps.onOpenWillChange = handleOpenWillChange
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const content = (
|
|
565
|
+
<z.menu.Root {...rootProps}>
|
|
566
|
+
{transformChildren(z.menu, z.componentMap, children, false, {
|
|
567
|
+
claim: claimTriggerBoundary,
|
|
568
|
+
release: releaseTriggerBoundary,
|
|
569
|
+
})}
|
|
570
|
+
</z.menu.Root>
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
if (isAndroid) {
|
|
574
|
+
return (
|
|
575
|
+
<NativeMenuContext.Provider value={true}>{content}</NativeMenuContext.Provider>
|
|
576
|
+
)
|
|
345
577
|
}
|
|
346
578
|
|
|
347
|
-
return
|
|
579
|
+
return content
|
|
348
580
|
}
|
|
349
581
|
Menu.displayName = MenuType
|
|
350
582
|
|
|
351
|
-
// ===========================================
|
|
352
|
-
// Export
|
|
353
|
-
// ===========================================
|
|
354
|
-
|
|
355
583
|
return {
|
|
356
584
|
Menu: withStaticProperties(Menu, {
|
|
357
585
|
Trigger,
|