@tamagui/create-menu 2.0.0-rc.3 → 2.0.0-rc.31
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 +1 -1
- package/dist/cjs/MenuPredefined.native.js +1 -1
- package/dist/cjs/createBaseMenu.cjs +90 -29
- package/dist/cjs/createBaseMenu.native.js +109 -34
- package/dist/cjs/createBaseMenu.native.js.map +1 -1
- package/dist/cjs/createNativeMenu/createNativeMenu.cjs +159 -128
- package/dist/cjs/createNativeMenu/createNativeMenu.native.js +258 -238
- package/dist/cjs/createNativeMenu/createNativeMenu.native.js.map +1 -1
- package/dist/cjs/createNativeMenu/withNativeMenu.cjs +2 -2
- package/dist/cjs/createNativeMenu/withNativeMenu.native.js +2 -2
- package/dist/cjs/createNativeMenu/withNativeMenu.native.js.map +1 -1
- package/dist/cjs/index.cjs +2 -1
- package/dist/cjs/index.native.js +2 -1
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/esm/MenuPredefined.mjs +1 -1
- package/dist/esm/MenuPredefined.native.js +1 -1
- package/dist/esm/createBaseMenu.mjs +91 -30
- package/dist/esm/createBaseMenu.mjs.map +1 -1
- package/dist/esm/createBaseMenu.native.js +110 -35
- package/dist/esm/createBaseMenu.native.js.map +1 -1
- package/dist/esm/createNativeMenu/createNativeMenu.mjs +160 -129
- package/dist/esm/createNativeMenu/createNativeMenu.mjs.map +1 -1
- package/dist/esm/createNativeMenu/createNativeMenu.native.js +228 -208
- package/dist/esm/createNativeMenu/createNativeMenu.native.js.map +1 -1
- package/dist/esm/createNativeMenu/withNativeMenu.mjs +2 -2
- package/dist/esm/createNativeMenu/withNativeMenu.mjs.map +1 -1
- package/dist/esm/createNativeMenu/withNativeMenu.native.js +2 -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 +1 -1
- package/dist/jsx/MenuPredefined.native.js +1 -1
- package/dist/jsx/createBaseMenu.mjs +91 -30
- package/dist/jsx/createBaseMenu.mjs.map +1 -1
- package/dist/jsx/createBaseMenu.native.js +109 -34
- package/dist/jsx/createBaseMenu.native.js.map +1 -1
- package/dist/jsx/createNativeMenu/createNativeMenu.mjs +160 -129
- package/dist/jsx/createNativeMenu/createNativeMenu.mjs.map +1 -1
- package/dist/jsx/createNativeMenu/createNativeMenu.native.js +258 -238
- package/dist/jsx/createNativeMenu/createNativeMenu.native.js.map +1 -1
- package/dist/jsx/createNativeMenu/withNativeMenu.mjs +2 -2
- package/dist/jsx/createNativeMenu/withNativeMenu.mjs.map +1 -1
- package/dist/jsx/createNativeMenu/withNativeMenu.native.js +2 -2
- 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 +2 -1
- package/dist/jsx/index.native.js.map +1 -1
- package/package.json +25 -27
- package/src/MenuPredefined.tsx +1 -1
- package/src/createBaseMenu.tsx +359 -271
- package/src/createNativeMenu/createNativeMenu.tsx +278 -219
- package/src/createNativeMenu/createNativeMenuTypes.ts +20 -20
- package/src/createNativeMenu/withNativeMenu.tsx +11 -4
- package/src/index.tsx +3 -5
- package/types/createBaseMenu.d.ts +121 -35
- 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 -832
- 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 -838
- 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 -838
- 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,214 @@
|
|
|
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 { getZeego } from '@tamagui/native'
|
|
8
|
+
import { getZeego, NativeMenuContext } from '@tamagui/native'
|
|
9
9
|
import { isWeb, withStaticProperties, isIos } from '@tamagui/web'
|
|
10
10
|
import type { FC } from 'react'
|
|
11
11
|
import React from 'react'
|
|
12
12
|
import type {
|
|
13
13
|
ContextMenuPreviewProps,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
14
|
+
NativeContextMenuAuxiliaryProps,
|
|
15
|
+
NativeMenuArrowProps,
|
|
16
|
+
NativeMenuCheckboxItemProps,
|
|
17
|
+
NativeMenuContentProps,
|
|
18
|
+
NativeMenuGroupProps,
|
|
19
|
+
NativeMenuItemIconProps,
|
|
20
|
+
NativeMenuItemImageProps,
|
|
21
|
+
NativeMenuItemIndicatorProps,
|
|
22
|
+
NativeMenuItemProps,
|
|
23
|
+
NativeMenuItemSubtitleProps,
|
|
24
|
+
NativeMenuItemTitleProps,
|
|
25
|
+
NativeMenuLabelProps,
|
|
26
|
+
NativeMenuProps,
|
|
27
|
+
NativeMenuSeparatorProps,
|
|
28
|
+
NativeMenuSubContentProps,
|
|
29
|
+
NativeMenuSubProps,
|
|
30
|
+
NativeMenuSubTriggerProps,
|
|
30
31
|
MenuTriggerProps,
|
|
31
32
|
} from './createNativeMenuTypes'
|
|
32
33
|
|
|
34
|
+
// zeego module shape (DropdownMenu / ContextMenu both share this)
|
|
35
|
+
type ZeegoMenuModule = {
|
|
36
|
+
Root: FC<Record<string, unknown>>
|
|
37
|
+
Trigger: FC<MenuTriggerProps>
|
|
38
|
+
Content: FC<NativeMenuContentProps>
|
|
39
|
+
Item: FC<NativeMenuItemProps>
|
|
40
|
+
ItemTitle: FC<NativeMenuItemTitleProps>
|
|
41
|
+
ItemSubtitle: FC<NativeMenuItemSubtitleProps>
|
|
42
|
+
ItemIcon: FC<NativeMenuItemIconProps>
|
|
43
|
+
ItemImage: FC<NativeMenuItemImageProps>
|
|
44
|
+
ItemIndicator: FC<NativeMenuItemIndicatorProps>
|
|
45
|
+
Group: FC<NativeMenuGroupProps>
|
|
46
|
+
Label: FC<NativeMenuLabelProps>
|
|
47
|
+
Separator: FC<NativeMenuSeparatorProps>
|
|
48
|
+
Sub: FC<NativeMenuSubProps>
|
|
49
|
+
SubTrigger: FC<NativeMenuSubTriggerProps>
|
|
50
|
+
SubContent: FC<NativeMenuSubContentProps>
|
|
51
|
+
CheckboxItem: FC<NativeMenuCheckboxItemProps>
|
|
52
|
+
Preview: FC<ContextMenuPreviewProps>
|
|
53
|
+
Auxiliary: FC<NativeContextMenuAuxiliaryProps>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// component types we recognize via displayName matching
|
|
57
|
+
type MappedComponentType =
|
|
58
|
+
| 'SubContent'
|
|
59
|
+
| 'SubTrigger'
|
|
60
|
+
| 'Content'
|
|
61
|
+
| 'Sub'
|
|
62
|
+
| 'Group'
|
|
63
|
+
| 'CheckboxItem'
|
|
64
|
+
|
|
65
|
+
const MAPPED_TYPES: MappedComponentType[] = [
|
|
66
|
+
'SubContent',
|
|
67
|
+
'SubTrigger',
|
|
68
|
+
'Content',
|
|
69
|
+
'Sub',
|
|
70
|
+
'Group',
|
|
71
|
+
'CheckboxItem',
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
// types whose children get recursively transformed
|
|
75
|
+
const CONTAINER_TYPES: MappedComponentType[] = ['SubContent', 'Content', 'Sub', 'Group']
|
|
76
|
+
|
|
77
|
+
type ComponentMap = Pick<
|
|
78
|
+
ZeegoMenuModule,
|
|
79
|
+
'SubContent' | 'Content' | 'Sub' | 'Group' | 'SubTrigger'
|
|
80
|
+
>
|
|
81
|
+
|
|
33
82
|
export type NativeMenuComponents = {
|
|
34
|
-
Menu: FC<
|
|
83
|
+
Menu: FC<NativeMenuProps> & {
|
|
35
84
|
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<
|
|
85
|
+
Content: FC<NativeMenuContentProps>
|
|
86
|
+
Item: FC<NativeMenuItemProps>
|
|
87
|
+
ItemTitle: FC<NativeMenuItemTitleProps>
|
|
88
|
+
ItemSubtitle: FC<NativeMenuItemSubtitleProps>
|
|
89
|
+
SubTrigger: FC<NativeMenuSubTriggerProps>
|
|
90
|
+
Group: FC<NativeMenuGroupProps>
|
|
91
|
+
ItemIcon: FC<NativeMenuItemIconProps>
|
|
92
|
+
Separator: FC<NativeMenuSeparatorProps>
|
|
93
|
+
CheckboxItem: FC<NativeMenuCheckboxItemProps>
|
|
94
|
+
ItemIndicator: FC<NativeMenuItemIndicatorProps>
|
|
95
|
+
ItemImage: FC<NativeMenuItemImageProps>
|
|
96
|
+
Label: FC<NativeMenuLabelProps>
|
|
97
|
+
Arrow: FC<NativeMenuArrowProps>
|
|
98
|
+
Sub: FC<NativeMenuSubProps>
|
|
99
|
+
SubContent: FC<NativeMenuSubContentProps>
|
|
51
100
|
Preview: FC<ContextMenuPreviewProps>
|
|
52
101
|
Portal: FC<{ children: React.ReactNode }>
|
|
53
102
|
RadioGroup: FC<{ children: React.ReactNode }>
|
|
54
103
|
RadioItem: FC<{ children: React.ReactNode }>
|
|
55
|
-
Auxiliary: FC<
|
|
104
|
+
Auxiliary: FC<NativeContextMenuAuxiliaryProps>
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// shared helpers (stateless, no need to recreate per call)
|
|
109
|
+
|
|
110
|
+
function getComponentType(displayName: string): MappedComponentType | null {
|
|
111
|
+
for (const type of MAPPED_TYPES) {
|
|
112
|
+
if (displayName === type || displayName.includes(`(${type})`)) {
|
|
113
|
+
return type
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return null
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function isItemLike(props: Record<string, unknown>, displayName: string): boolean {
|
|
120
|
+
if (getComponentType(displayName)) return false
|
|
121
|
+
return 'onSelect' in props || 'textValue' in props
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function isPortalLike(displayName: string): boolean {
|
|
125
|
+
return displayName === 'Portal' || displayName.includes('Portal')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// stub used for web — never actually rendered, just needs to exist for withNativeMenu fallback
|
|
129
|
+
const emptyStub = (() => null) as FC<any>
|
|
130
|
+
|
|
131
|
+
function createWebStubs(): NativeMenuComponents {
|
|
132
|
+
return {
|
|
133
|
+
Menu: withStaticProperties(emptyStub as FC<NativeMenuProps>, {
|
|
134
|
+
Trigger: emptyStub as FC<MenuTriggerProps>,
|
|
135
|
+
Content: emptyStub as FC<NativeMenuContentProps>,
|
|
136
|
+
Item: emptyStub as FC<NativeMenuItemProps>,
|
|
137
|
+
ItemTitle: emptyStub as FC<NativeMenuItemTitleProps>,
|
|
138
|
+
ItemSubtitle: emptyStub as FC<NativeMenuItemSubtitleProps>,
|
|
139
|
+
SubTrigger: emptyStub as FC<NativeMenuSubTriggerProps>,
|
|
140
|
+
Group: emptyStub as FC<NativeMenuGroupProps>,
|
|
141
|
+
ItemIcon: emptyStub as FC<NativeMenuItemIconProps>,
|
|
142
|
+
Separator: emptyStub as FC<NativeMenuSeparatorProps>,
|
|
143
|
+
CheckboxItem: emptyStub as FC<NativeMenuCheckboxItemProps>,
|
|
144
|
+
ItemIndicator: emptyStub as FC<NativeMenuItemIndicatorProps>,
|
|
145
|
+
ItemImage: emptyStub as FC<NativeMenuItemImageProps>,
|
|
146
|
+
Label: emptyStub as FC<NativeMenuLabelProps>,
|
|
147
|
+
Arrow: emptyStub as FC<NativeMenuArrowProps>,
|
|
148
|
+
Sub: emptyStub as FC<NativeMenuSubProps>,
|
|
149
|
+
SubContent: emptyStub as FC<NativeMenuSubContentProps>,
|
|
150
|
+
Preview: emptyStub as FC<ContextMenuPreviewProps>,
|
|
151
|
+
Portal: emptyStub as FC<{ children: React.ReactNode }>,
|
|
152
|
+
RadioGroup: emptyStub as FC<{ children: React.ReactNode }>,
|
|
153
|
+
RadioItem: emptyStub as FC<{ children: React.ReactNode }>,
|
|
154
|
+
Auxiliary: emptyStub as FC<NativeContextMenuAuxiliaryProps>,
|
|
155
|
+
}),
|
|
56
156
|
}
|
|
57
157
|
}
|
|
58
158
|
|
|
59
159
|
export const createNativeMenu = (
|
|
60
160
|
MenuType: 'ContextMenu' | 'Menu'
|
|
61
161
|
): NativeMenuComponents => {
|
|
62
|
-
// On web, return empty stubs - withNativeMenu will use the web components passed to it
|
|
63
162
|
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
|
-
}
|
|
163
|
+
return createWebStubs()
|
|
112
164
|
}
|
|
113
165
|
|
|
114
166
|
// ===========================================
|
|
115
|
-
//
|
|
167
|
+
// native implementation — lazily resolves zeego
|
|
116
168
|
// ===========================================
|
|
117
169
|
|
|
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
170
|
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
|
|
171
|
+
const isAndroid = !isIos && !isWeb
|
|
172
|
+
|
|
173
|
+
// cached after first successful resolve
|
|
174
|
+
let resolved: { menu: ZeegoMenuModule; componentMap: ComponentMap } | null = null
|
|
175
|
+
let warned = false
|
|
176
|
+
|
|
177
|
+
function resolve(): typeof resolved {
|
|
178
|
+
if (resolved) return resolved
|
|
179
|
+
const zeego = getZeego()
|
|
180
|
+
if (!zeego.isEnabled) {
|
|
181
|
+
if (!warned) {
|
|
182
|
+
warned = true
|
|
183
|
+
console.warn(
|
|
184
|
+
`Warning: Must call import '@tamagui/native/setup-zeego' at your app entry point to use native menus`
|
|
185
|
+
)
|
|
158
186
|
}
|
|
187
|
+
return null
|
|
159
188
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
189
|
+
const menu = (
|
|
190
|
+
isContextMenu ? zeego.state.ContextMenu : zeego.state.DropdownMenu
|
|
191
|
+
) as ZeegoMenuModule
|
|
192
|
+
resolved = {
|
|
193
|
+
menu,
|
|
194
|
+
componentMap: {
|
|
195
|
+
SubContent: menu.SubContent,
|
|
196
|
+
Content: menu.Content,
|
|
197
|
+
Sub: menu.Sub,
|
|
198
|
+
Group: menu.Group,
|
|
199
|
+
SubTrigger: menu.SubTrigger,
|
|
200
|
+
},
|
|
170
201
|
}
|
|
171
|
-
return
|
|
202
|
+
return resolved
|
|
172
203
|
}
|
|
173
204
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
return displayName === 'Portal' || displayName.includes('Portal')
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Transform children tree for Zeego compatibility:
|
|
183
|
-
* - Flatten Portal wrappers
|
|
184
|
-
* - Recurse into containers (Content, Sub, Group, SubContent)
|
|
185
|
-
* - Convert styled Items to Zeego Items
|
|
186
|
-
* - Reverse children on iOS only for DropdownMenu at Content/SubContent level
|
|
187
|
-
*/
|
|
188
|
-
const transformForZeego = (
|
|
205
|
+
// transform children tree for zeego compatibility
|
|
206
|
+
function transformChildren(
|
|
207
|
+
menu: ZeegoMenuModule,
|
|
208
|
+
map: ComponentMap,
|
|
189
209
|
children: React.ReactNode,
|
|
190
210
|
shouldReverseOnIos = false
|
|
191
|
-
): React.ReactNode
|
|
211
|
+
): React.ReactNode {
|
|
192
212
|
const result: React.ReactNode[] = []
|
|
193
213
|
|
|
194
214
|
React.Children.forEach(children, (child) => {
|
|
@@ -197,49 +217,67 @@ export const createNativeMenu = (
|
|
|
197
217
|
return
|
|
198
218
|
}
|
|
199
219
|
|
|
200
|
-
const displayName = (child.type as
|
|
220
|
+
const displayName = (child.type as { displayName?: string })?.displayName || ''
|
|
201
221
|
const props = child.props as Record<string, any>
|
|
202
222
|
|
|
203
|
-
//
|
|
204
|
-
if (
|
|
205
|
-
const
|
|
206
|
-
|
|
223
|
+
// flatten portal wrappers
|
|
224
|
+
if (isPortalLike(displayName)) {
|
|
225
|
+
const inner = transformChildren(
|
|
226
|
+
menu,
|
|
227
|
+
map,
|
|
228
|
+
props.children as React.ReactNode,
|
|
229
|
+
false
|
|
230
|
+
)
|
|
231
|
+
React.Children.forEach(inner, (c) => result.push(c))
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// flatten ScrollView (native passthrough — children need to be visible to zeego)
|
|
236
|
+
if (displayName.includes('ScrollView')) {
|
|
237
|
+
const inner = transformChildren(
|
|
238
|
+
menu,
|
|
239
|
+
map,
|
|
240
|
+
props.children as React.ReactNode,
|
|
241
|
+
false
|
|
242
|
+
)
|
|
243
|
+
React.Children.forEach(inner, (c) => result.push(c))
|
|
207
244
|
return
|
|
208
245
|
}
|
|
209
246
|
|
|
210
|
-
// Handle known component types (containers, SubTrigger, CheckboxItem)
|
|
211
247
|
const componentType = getComponentType(displayName)
|
|
212
248
|
|
|
213
|
-
//
|
|
249
|
+
// normalize checkbox checked/value props
|
|
214
250
|
if (componentType === 'CheckboxItem') {
|
|
215
|
-
const {
|
|
216
|
-
|
|
251
|
+
const {
|
|
252
|
+
checked,
|
|
253
|
+
onCheckedChange,
|
|
254
|
+
value,
|
|
255
|
+
onValueChange,
|
|
256
|
+
children: cbChildren,
|
|
257
|
+
...rest
|
|
258
|
+
} = props as Record<string, any>
|
|
217
259
|
|
|
218
260
|
const finalValue = value ?? (checked ? 'on' : 'off')
|
|
219
261
|
const finalOnValueChange =
|
|
220
262
|
onValueChange ??
|
|
221
263
|
(onCheckedChange && ((v: string) => onCheckedChange(v === 'on')))
|
|
222
264
|
|
|
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
|
|
265
|
+
const cleanChildren = React.Children.map(cbChildren, (c) => {
|
|
266
|
+
if (!React.isValidElement(c)) return c
|
|
267
|
+
const dn = (c.type as { displayName?: string })?.displayName || ''
|
|
268
|
+
if (dn.includes('ItemIndicator')) return null
|
|
269
|
+
return c
|
|
232
270
|
})
|
|
233
271
|
|
|
234
272
|
result.push(
|
|
235
273
|
React.createElement(
|
|
236
|
-
|
|
274
|
+
menu.CheckboxItem,
|
|
237
275
|
{
|
|
238
276
|
...rest,
|
|
239
277
|
key: child.key,
|
|
240
278
|
value: finalValue,
|
|
241
279
|
onValueChange: finalOnValueChange,
|
|
242
|
-
},
|
|
280
|
+
} as any,
|
|
243
281
|
cleanChildren
|
|
244
282
|
)
|
|
245
283
|
)
|
|
@@ -248,41 +286,43 @@ export const createNativeMenu = (
|
|
|
248
286
|
|
|
249
287
|
if (componentType) {
|
|
250
288
|
const { children: childChildren, ...restProps } = props
|
|
251
|
-
const isContainer = CONTAINER_TYPES.includes(componentType)
|
|
252
|
-
|
|
253
|
-
const shouldReverseChildren =
|
|
289
|
+
const isContainer = (CONTAINER_TYPES as string[]).includes(componentType)
|
|
290
|
+
const shouldReverse =
|
|
254
291
|
componentType === 'Content' || componentType === 'SubContent'
|
|
255
292
|
result.push(
|
|
256
293
|
React.createElement(
|
|
257
|
-
|
|
258
|
-
{ ...restProps, key: child.key },
|
|
294
|
+
map[componentType as keyof ComponentMap],
|
|
295
|
+
{ ...restProps, key: child.key } as any,
|
|
259
296
|
isContainer
|
|
260
|
-
?
|
|
297
|
+
? transformChildren(
|
|
298
|
+
menu,
|
|
299
|
+
map,
|
|
300
|
+
childChildren as React.ReactNode,
|
|
301
|
+
shouldReverse
|
|
302
|
+
)
|
|
261
303
|
: childChildren
|
|
262
304
|
)
|
|
263
305
|
)
|
|
264
306
|
return
|
|
265
307
|
}
|
|
266
308
|
|
|
267
|
-
//
|
|
309
|
+
// convert Item-like components to zeego Items
|
|
268
310
|
if (isItemLike(props, displayName)) {
|
|
269
311
|
const { children: itemChildren, ...itemProps } = props
|
|
270
312
|
result.push(
|
|
271
313
|
React.createElement(
|
|
272
|
-
|
|
273
|
-
{ ...itemProps, key: child.key },
|
|
314
|
+
menu.Item,
|
|
315
|
+
{ ...itemProps, key: child.key } as any,
|
|
274
316
|
itemChildren
|
|
275
317
|
)
|
|
276
318
|
)
|
|
277
319
|
return
|
|
278
320
|
}
|
|
279
321
|
|
|
280
|
-
// Pass through everything else
|
|
281
322
|
result.push(child)
|
|
282
323
|
})
|
|
283
324
|
|
|
284
|
-
// iOS DropdownMenu
|
|
285
|
-
// Only reverse for Menu component, not ContextMenu
|
|
325
|
+
// iOS DropdownMenu displays items in reverse order
|
|
286
326
|
if (isIos && shouldReverseOnIos && !isContextMenu) {
|
|
287
327
|
result.reverse()
|
|
288
328
|
}
|
|
@@ -290,31 +330,39 @@ export const createNativeMenu = (
|
|
|
290
330
|
return result
|
|
291
331
|
}
|
|
292
332
|
|
|
293
|
-
//
|
|
294
|
-
|
|
295
|
-
|
|
333
|
+
// lazy wrapper — resolves the zeego component on first render
|
|
334
|
+
function lazyZeego<P extends Record<string, any>>(
|
|
335
|
+
name: keyof ZeegoMenuModule,
|
|
336
|
+
displayName?: string
|
|
337
|
+
): FC<P> {
|
|
338
|
+
const Comp: FC<P> = (props) => {
|
|
339
|
+
const z = resolve()
|
|
340
|
+
if (!z) return null
|
|
341
|
+
return React.createElement(z.menu[name] as FC<any>, props)
|
|
342
|
+
}
|
|
343
|
+
Comp.displayName = displayName || name
|
|
344
|
+
return Comp
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const Trigger = lazyZeego<MenuTriggerProps>('Trigger')
|
|
348
|
+
const Content = lazyZeego<NativeMenuContentProps>('Content')
|
|
349
|
+
const Item = lazyZeego<NativeMenuItemProps>('Item')
|
|
350
|
+
const ItemTitle = lazyZeego<NativeMenuItemTitleProps>('ItemTitle')
|
|
351
|
+
const ItemSubtitle = lazyZeego<NativeMenuItemSubtitleProps>('ItemSubtitle')
|
|
352
|
+
const ItemIcon = lazyZeego<NativeMenuItemIconProps>('ItemIcon')
|
|
353
|
+
const ItemImage = lazyZeego<NativeMenuItemImageProps>('ItemImage')
|
|
354
|
+
const ItemIndicator = lazyZeego<NativeMenuItemIndicatorProps>('ItemIndicator')
|
|
355
|
+
const Group = lazyZeego<NativeMenuGroupProps>('Group')
|
|
356
|
+
const Label = lazyZeego<NativeMenuLabelProps>('Label')
|
|
357
|
+
const Separator = lazyZeego<NativeMenuSeparatorProps>('Separator')
|
|
358
|
+
const Sub = lazyZeego<NativeMenuSubProps>('Sub')
|
|
359
|
+
const SubTrigger = lazyZeego<NativeMenuSubTriggerProps>('SubTrigger')
|
|
360
|
+
const SubContent = lazyZeego<NativeMenuSubContentProps>('SubContent')
|
|
296
361
|
|
|
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
362
|
const Portal: FC<{ children: React.ReactNode }> = ({ children }) => <>{children}</>
|
|
315
363
|
Portal.displayName = 'Portal'
|
|
316
364
|
|
|
317
|
-
const Arrow: FC<
|
|
365
|
+
const Arrow: FC<NativeMenuArrowProps> = () => null
|
|
318
366
|
Arrow.displayName = 'Arrow'
|
|
319
367
|
|
|
320
368
|
const RadioGroup: FC<{ children: React.ReactNode }> = ({ children }) => <>{children}</>
|
|
@@ -322,36 +370,47 @@ export const createNativeMenu = (
|
|
|
322
370
|
|
|
323
371
|
const RadioItem: FC<{ children: React.ReactNode }> = ({ children }) => <>{children}</>
|
|
324
372
|
RadioItem.displayName = `${MenuType}RadioItem`
|
|
325
|
-
|
|
326
|
-
const CheckboxItem: FC<
|
|
373
|
+
|
|
374
|
+
const CheckboxItem: FC<NativeMenuCheckboxItemProps> = () => null
|
|
327
375
|
CheckboxItem.displayName = 'CheckboxItem'
|
|
328
376
|
|
|
329
|
-
// Context menu specific
|
|
330
377
|
const Preview: FC<ContextMenuPreviewProps> = isContextMenu
|
|
331
|
-
?
|
|
378
|
+
? lazyZeego<ContextMenuPreviewProps>('Preview', `${MenuType}Preview`)
|
|
332
379
|
: () => null
|
|
333
380
|
Preview.displayName = `${MenuType}Preview`
|
|
334
381
|
|
|
335
|
-
const Auxiliary: FC<
|
|
336
|
-
?
|
|
382
|
+
const Auxiliary: FC<NativeContextMenuAuxiliaryProps> = isContextMenu
|
|
383
|
+
? lazyZeego<NativeContextMenuAuxiliaryProps>('Auxiliary', `${MenuType}Auxiliary`)
|
|
337
384
|
: () => null
|
|
338
385
|
Auxiliary.displayName = `${MenuType}Auxiliary`
|
|
339
386
|
|
|
340
|
-
//
|
|
341
|
-
|
|
387
|
+
// on Android, provide NativeMenuContext so components use Gesture.Manual()
|
|
388
|
+
// instead of Gesture.Tap() (which sends ACTION_CANCEL to MenuView)
|
|
389
|
+
const Menu: FC<NativeMenuProps> = ({ children, onOpenChange, onOpenWillChange }) => {
|
|
390
|
+
const z = resolve()
|
|
391
|
+
if (!z) return null
|
|
392
|
+
|
|
342
393
|
const rootProps: Record<string, unknown> = { onOpenChange }
|
|
343
394
|
if (isContextMenu && onOpenWillChange) {
|
|
344
395
|
rootProps.onOpenWillChange = onOpenWillChange
|
|
345
396
|
}
|
|
346
397
|
|
|
347
|
-
|
|
398
|
+
const content = (
|
|
399
|
+
<z.menu.Root {...rootProps}>
|
|
400
|
+
{transformChildren(z.menu, z.componentMap, children)}
|
|
401
|
+
</z.menu.Root>
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
if (isAndroid) {
|
|
405
|
+
return (
|
|
406
|
+
<NativeMenuContext.Provider value={true}>{content}</NativeMenuContext.Provider>
|
|
407
|
+
)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return content
|
|
348
411
|
}
|
|
349
412
|
Menu.displayName = MenuType
|
|
350
413
|
|
|
351
|
-
// ===========================================
|
|
352
|
-
// Export
|
|
353
|
-
// ===========================================
|
|
354
|
-
|
|
355
414
|
return {
|
|
356
415
|
Menu: withStaticProperties(Menu, {
|
|
357
416
|
Trigger,
|