@tamagui/native 2.6.4 → 2.7.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/.turbo/turbo-build.log +1 -1
- package/dist/cjs/expoUIMenuAdapter.cjs +265 -0
- package/dist/cjs/expoUIMenuAdapter.native.js +321 -0
- package/dist/cjs/expoUIMenuAdapter.native.js.map +1 -0
- package/dist/cjs/index.cjs +5 -2
- package/dist/cjs/index.native.js +5 -2
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/{zeegoState.cjs → nativeMenuState.cjs} +19 -19
- package/dist/cjs/{zeegoState.native.js → nativeMenuState.native.js} +20 -20
- package/dist/cjs/nativeMenuState.native.js.map +1 -0
- package/dist/cjs/setup-expo-ui-menu.cjs +17 -0
- package/dist/cjs/setup-expo-ui-menu.native.js +20 -0
- package/dist/cjs/setup-expo-ui-menu.native.js.map +1 -0
- package/dist/cjs/setup-zeego.cjs +11 -13
- package/dist/cjs/setup-zeego.native.js +11 -13
- package/dist/cjs/setup-zeego.native.js.map +1 -1
- package/dist/esm/expoUIMenuAdapter.mjs +229 -0
- package/dist/esm/expoUIMenuAdapter.mjs.map +1 -0
- package/dist/esm/expoUIMenuAdapter.native.js +282 -0
- package/dist/esm/expoUIMenuAdapter.native.js.map +1 -0
- package/dist/esm/index.js +3 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs +3 -2
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +3 -2
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/nativeMenuState.mjs +20 -0
- package/dist/esm/nativeMenuState.mjs.map +1 -0
- package/dist/esm/nativeMenuState.native.js +20 -0
- package/dist/esm/nativeMenuState.native.js.map +1 -0
- package/dist/esm/setup-expo-ui-menu.mjs +18 -0
- package/dist/esm/setup-expo-ui-menu.mjs.map +1 -0
- package/dist/esm/setup-expo-ui-menu.native.js +18 -0
- package/dist/esm/setup-expo-ui-menu.native.js.map +1 -0
- package/dist/esm/setup-zeego.mjs +11 -13
- package/dist/esm/setup-zeego.mjs.map +1 -1
- package/dist/esm/setup-zeego.native.js +11 -13
- package/dist/esm/setup-zeego.native.js.map +1 -1
- package/package.json +14 -2
- package/setup-expo-ui-menu/index.cjs +1 -0
- package/setup-expo-ui-menu/index.js +1 -0
- package/setup-expo-ui-menu/index.native.cjs +1 -0
- package/setup-expo-ui-menu/index.native.js +1 -0
- package/setup-expo-ui-menu.cjs +1 -0
- package/src/expoUIMenuAdapter.tsx +320 -0
- package/src/index.ts +5 -4
- package/src/nativeMenuState.ts +63 -0
- package/src/setup-expo-ui-menu.ts +25 -0
- package/src/setup-zeego.ts +12 -16
- package/src/types.ts +0 -6
- package/types/expoUIMenuAdapter.d.ts +8 -0
- package/types/expoUIMenuAdapter.d.ts.map +11 -0
- package/types/index.d.ts +5 -3
- package/types/index.d.ts.map +2 -2
- package/types/nativeMenuState.d.ts +38 -0
- package/types/nativeMenuState.d.ts.map +11 -0
- package/types/setup-expo-ui-menu.d.ts +3 -0
- package/types/setup-expo-ui-menu.d.ts.map +11 -0
- package/types/setup-zeego.d.ts.map +1 -1
- package/types/types.d.ts +0 -5
- package/types/types.d.ts.map +2 -2
- package/dist/cjs/zeegoState.native.js.map +0 -1
- package/dist/esm/zeegoState.mjs +0 -21
- package/dist/esm/zeegoState.mjs.map +0 -1
- package/dist/esm/zeegoState.native.js +0 -21
- package/dist/esm/zeegoState.native.js.map +0 -1
- package/src/zeegoState.ts +0 -28
- package/types/zeegoState.d.ts +0 -9
- package/types/zeegoState.d.ts.map +0 -11
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import type { ComponentType, ReactElement, ReactNode } from 'react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
import type { NativeMenuAdapter, NativeMenuModule } from './nativeMenuState'
|
|
5
|
+
|
|
6
|
+
type ExpoMenuAction = {
|
|
7
|
+
id: string
|
|
8
|
+
title: string
|
|
9
|
+
image?: unknown
|
|
10
|
+
state?: 'on' | 'off'
|
|
11
|
+
attributes?: {
|
|
12
|
+
destructive?: boolean
|
|
13
|
+
disabled?: boolean
|
|
14
|
+
hidden?: boolean
|
|
15
|
+
}
|
|
16
|
+
subactions?: ExpoMenuAction[]
|
|
17
|
+
displayInline?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type ExpoMenuViewProps = {
|
|
21
|
+
actions: ExpoMenuAction[]
|
|
22
|
+
title?: string
|
|
23
|
+
shouldOpenOnLongPress?: boolean
|
|
24
|
+
onPressAction?: (event: { nativeEvent: { event: string } }) => void
|
|
25
|
+
onOpenMenu?: () => void
|
|
26
|
+
onCloseMenu?: () => void
|
|
27
|
+
children?: ReactNode
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type ExpoUIMenuAdapterOptions = {
|
|
31
|
+
MenuView: ComponentType<any>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type MarkerProps = {
|
|
35
|
+
children?: ReactNode
|
|
36
|
+
[key: string]: unknown
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function marker(name: string): ComponentType<MarkerProps> {
|
|
40
|
+
const Marker = () => null
|
|
41
|
+
Marker.displayName = `ExpoUI${name}`
|
|
42
|
+
return Marker
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function elementOfType(
|
|
46
|
+
children: ReactNode,
|
|
47
|
+
type: ComponentType<MarkerProps>
|
|
48
|
+
): ReactElement<MarkerProps> | null {
|
|
49
|
+
for (const child of React.Children.toArray(children)) {
|
|
50
|
+
if (React.isValidElement<MarkerProps>(child) && child.type === type) {
|
|
51
|
+
return child
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function textFromNode(node: ReactNode): string {
|
|
58
|
+
if (typeof node === 'string' || typeof node === 'number') return String(node)
|
|
59
|
+
if (!React.isValidElement<MarkerProps>(node)) return ''
|
|
60
|
+
return React.Children.toArray(node.props.children).map(textFromNode).join('')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createExpoUIMenuAdapter({
|
|
64
|
+
MenuView,
|
|
65
|
+
}: ExpoUIMenuAdapterOptions): NativeMenuAdapter {
|
|
66
|
+
const NativeMenuView: ComponentType<ExpoMenuViewProps> = MenuView
|
|
67
|
+
|
|
68
|
+
function createModule(isContextMenu: boolean): NativeMenuModule {
|
|
69
|
+
const Trigger = marker('Trigger')
|
|
70
|
+
const Content = marker('Content')
|
|
71
|
+
const Item = marker('Item')
|
|
72
|
+
const ItemTitle = marker('ItemTitle')
|
|
73
|
+
const ItemSubtitle = marker('ItemSubtitle')
|
|
74
|
+
const ItemIcon = marker('ItemIcon')
|
|
75
|
+
const ItemImage = marker('ItemImage')
|
|
76
|
+
const ItemIndicator = marker('ItemIndicator')
|
|
77
|
+
const Group = marker('Group')
|
|
78
|
+
const Label = marker('Label')
|
|
79
|
+
const Separator = marker('Separator')
|
|
80
|
+
const Sub = marker('Sub')
|
|
81
|
+
const SubTrigger = marker('SubTrigger')
|
|
82
|
+
const SubContent = marker('SubContent')
|
|
83
|
+
const CheckboxItem = marker('CheckboxItem')
|
|
84
|
+
const Preview = marker('Preview')
|
|
85
|
+
const Auxiliary = marker('Auxiliary')
|
|
86
|
+
|
|
87
|
+
function Root({
|
|
88
|
+
children,
|
|
89
|
+
onOpenChange,
|
|
90
|
+
onOpenWillChange,
|
|
91
|
+
}: {
|
|
92
|
+
children?: ReactNode
|
|
93
|
+
onOpenChange?: (open: boolean) => void
|
|
94
|
+
onOpenWillChange?: (open: boolean) => void
|
|
95
|
+
}) {
|
|
96
|
+
const trigger = elementOfType(children, Trigger)
|
|
97
|
+
const content = elementOfType(children, Content)
|
|
98
|
+
const preview = elementOfType(children, Preview)
|
|
99
|
+
const auxiliary = elementOfType(children, Auxiliary)
|
|
100
|
+
|
|
101
|
+
if (preview || auxiliary) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
'@expo/ui/community/menu does not support Tamagui context-menu previews or auxiliary views'
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
if (!trigger || !content) {
|
|
107
|
+
throw new Error('Tamagui native menus require one Trigger and one Content')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let nextId = 0
|
|
111
|
+
const handlers = new Map<string, () => void>()
|
|
112
|
+
|
|
113
|
+
function actionId(element: ReactElement<MarkerProps>): string {
|
|
114
|
+
return element.key == null
|
|
115
|
+
? `tamagui-menu-${nextId++}`
|
|
116
|
+
: String(element.key).replace(/^\.\$/, '')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function itemTitle(element: ReactElement<MarkerProps>): string {
|
|
120
|
+
if (typeof element.props.textValue === 'string') return element.props.textValue
|
|
121
|
+
const title = elementOfType(element.props.children, ItemTitle)
|
|
122
|
+
return title ? textFromNode(title.props.children) : ''
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function actionImage(element: ReactElement<MarkerProps>): unknown {
|
|
126
|
+
const image = elementOfType(element.props.children, ItemImage)
|
|
127
|
+
if (image?.props.source) return image.props.source
|
|
128
|
+
const icon = elementOfType(element.props.children, ItemIcon)
|
|
129
|
+
if (!icon) return undefined
|
|
130
|
+
const ios = icon.props.ios
|
|
131
|
+
if (
|
|
132
|
+
typeof ios === 'object' &&
|
|
133
|
+
ios &&
|
|
134
|
+
'name' in ios &&
|
|
135
|
+
typeof ios.name === 'string'
|
|
136
|
+
) {
|
|
137
|
+
return ios.name
|
|
138
|
+
}
|
|
139
|
+
return typeof icon.props.iosIconName === 'string'
|
|
140
|
+
? icon.props.iosIconName
|
|
141
|
+
: undefined
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function attributes(
|
|
145
|
+
element: ReactElement<MarkerProps>
|
|
146
|
+
): ExpoMenuAction['attributes'] {
|
|
147
|
+
const destructive = element.props.destructive === true
|
|
148
|
+
const disabled = element.props.disabled === true
|
|
149
|
+
const hidden = element.props.hidden === true
|
|
150
|
+
return destructive || disabled || hidden
|
|
151
|
+
? { destructive, disabled, hidden }
|
|
152
|
+
: undefined
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function actionsFrom(childrenToParse: ReactNode): ExpoMenuAction[] {
|
|
156
|
+
const actions: ExpoMenuAction[] = []
|
|
157
|
+
let section: ExpoMenuAction[] | null = null
|
|
158
|
+
|
|
159
|
+
function append(action: ExpoMenuAction) {
|
|
160
|
+
if (section) {
|
|
161
|
+
section.push(action)
|
|
162
|
+
} else {
|
|
163
|
+
actions.push(action)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function flushSection() {
|
|
168
|
+
if (!section?.length) {
|
|
169
|
+
section = []
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
actions.push({
|
|
173
|
+
id: `tamagui-menu-section-${nextId++}`,
|
|
174
|
+
title: '',
|
|
175
|
+
subactions: section,
|
|
176
|
+
displayInline: true,
|
|
177
|
+
})
|
|
178
|
+
section = []
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
for (const child of React.Children.toArray(childrenToParse)) {
|
|
182
|
+
if (!React.isValidElement<MarkerProps>(child)) continue
|
|
183
|
+
|
|
184
|
+
if (child.type === Separator) {
|
|
185
|
+
if (!section) section = actions.splice(0)
|
|
186
|
+
flushSection()
|
|
187
|
+
continue
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (child.type === Group) {
|
|
191
|
+
const label = elementOfType(child.props.children, Label)
|
|
192
|
+
append({
|
|
193
|
+
id: actionId(child),
|
|
194
|
+
title: label ? textFromNode(label.props.children) : '',
|
|
195
|
+
subactions: actionsFrom(child.props.children),
|
|
196
|
+
displayInline: true,
|
|
197
|
+
})
|
|
198
|
+
continue
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (child.type === Sub) {
|
|
202
|
+
const subTrigger = elementOfType(child.props.children, SubTrigger)
|
|
203
|
+
const subContent = elementOfType(child.props.children, SubContent)
|
|
204
|
+
if (!subTrigger || !subContent) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
'Tamagui native submenus require one SubTrigger and SubContent'
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
append({
|
|
210
|
+
id: actionId(child),
|
|
211
|
+
title: itemTitle(subTrigger),
|
|
212
|
+
image: actionImage(subTrigger),
|
|
213
|
+
attributes: attributes(subTrigger),
|
|
214
|
+
subactions: actionsFrom(subContent.props.children),
|
|
215
|
+
})
|
|
216
|
+
continue
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (child.type !== Item && child.type !== CheckboxItem) continue
|
|
220
|
+
|
|
221
|
+
if (elementOfType(child.props.children, ItemSubtitle)) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
'@expo/ui/community/menu does not support menu item subtitles'
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const id = actionId(child)
|
|
228
|
+
if (child.type === CheckboxItem) {
|
|
229
|
+
const current =
|
|
230
|
+
child.props.value === true ||
|
|
231
|
+
child.props.value === 'on' ||
|
|
232
|
+
child.props.checked === true
|
|
233
|
+
const onValueChange = child.props.onValueChange
|
|
234
|
+
const onCheckedChange = child.props.onCheckedChange
|
|
235
|
+
handlers.set(id, () => {
|
|
236
|
+
if (typeof onValueChange === 'function') {
|
|
237
|
+
onValueChange(current ? 'off' : 'on', current ? 'on' : 'off')
|
|
238
|
+
} else if (typeof onCheckedChange === 'function') {
|
|
239
|
+
onCheckedChange(!current)
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
append({
|
|
243
|
+
id,
|
|
244
|
+
title: itemTitle(child),
|
|
245
|
+
image: actionImage(child),
|
|
246
|
+
state: current ? 'on' : 'off',
|
|
247
|
+
attributes: attributes(child),
|
|
248
|
+
})
|
|
249
|
+
continue
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const onSelect = child.props.onSelect
|
|
253
|
+
if (typeof onSelect === 'function') {
|
|
254
|
+
handlers.set(id, () => onSelect())
|
|
255
|
+
}
|
|
256
|
+
append({
|
|
257
|
+
id,
|
|
258
|
+
title: itemTitle(child),
|
|
259
|
+
image: actionImage(child),
|
|
260
|
+
attributes: attributes(child),
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (section) flushSection()
|
|
265
|
+
return actions
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const label = elementOfType(content.props.children, Label)
|
|
269
|
+
const triggerAction = trigger.props.action
|
|
270
|
+
|
|
271
|
+
return (
|
|
272
|
+
<NativeMenuView
|
|
273
|
+
actions={actionsFrom(content.props.children)}
|
|
274
|
+
title={label ? textFromNode(label.props.children) : undefined}
|
|
275
|
+
shouldOpenOnLongPress={
|
|
276
|
+
triggerAction === 'longPress' || (triggerAction !== 'press' && isContextMenu)
|
|
277
|
+
}
|
|
278
|
+
onPressAction={(event) => handlers.get(event.nativeEvent.event)?.()}
|
|
279
|
+
onOpenMenu={() => {
|
|
280
|
+
onOpenWillChange?.(true)
|
|
281
|
+
onOpenChange?.(true)
|
|
282
|
+
}}
|
|
283
|
+
onCloseMenu={() => {
|
|
284
|
+
onOpenWillChange?.(false)
|
|
285
|
+
onOpenChange?.(false)
|
|
286
|
+
}}
|
|
287
|
+
>
|
|
288
|
+
{trigger.props.children}
|
|
289
|
+
</NativeMenuView>
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
Root,
|
|
295
|
+
Trigger,
|
|
296
|
+
Content,
|
|
297
|
+
Item,
|
|
298
|
+
ItemTitle,
|
|
299
|
+
ItemSubtitle,
|
|
300
|
+
ItemIcon,
|
|
301
|
+
ItemImage,
|
|
302
|
+
ItemIndicator,
|
|
303
|
+
Group,
|
|
304
|
+
Label,
|
|
305
|
+
Separator,
|
|
306
|
+
Sub,
|
|
307
|
+
SubTrigger,
|
|
308
|
+
SubContent,
|
|
309
|
+
CheckboxItem,
|
|
310
|
+
Preview,
|
|
311
|
+
Auxiliary,
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
name: 'expo-ui',
|
|
317
|
+
Menu: createModule(false),
|
|
318
|
+
ContextMenu: createModule(true),
|
|
319
|
+
}
|
|
320
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -29,12 +29,12 @@ export type {
|
|
|
29
29
|
SafeAreaFrame,
|
|
30
30
|
SafeAreaMetrics,
|
|
31
31
|
LinearGradientState,
|
|
32
|
-
ZeegoState,
|
|
33
32
|
BurntState,
|
|
34
33
|
NativePortalProps,
|
|
35
34
|
NativePortalHostProps,
|
|
36
35
|
NativePortalProviderProps,
|
|
37
36
|
} from './types'
|
|
37
|
+
export type { NativeMenuAdapter, NativeMenuModule } from './nativeMenuState'
|
|
38
38
|
|
|
39
39
|
// portal
|
|
40
40
|
export { getPortal } from './portalState'
|
|
@@ -76,9 +76,10 @@ export {
|
|
|
76
76
|
} from './keyboardControllerState'
|
|
77
77
|
export type { KeyboardControllerState } from './keyboardControllerState'
|
|
78
78
|
|
|
79
|
-
//
|
|
80
|
-
export {
|
|
81
|
-
export
|
|
79
|
+
// native menus
|
|
80
|
+
export { getNativeMenuAdapter, registerNativeMenuAdapter } from './nativeMenuState'
|
|
81
|
+
export { createExpoUIMenuAdapter } from './expoUIMenuAdapter'
|
|
82
|
+
export type { ExpoUIMenuAdapterOptions } from './expoUIMenuAdapter'
|
|
82
83
|
export { NativeMenuContext } from './nativeMenuContext'
|
|
83
84
|
|
|
84
85
|
// burnt (native toasts)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ComponentType } from 'react'
|
|
2
|
+
|
|
3
|
+
import { createGlobalState } from './globalState'
|
|
4
|
+
|
|
5
|
+
export type NativeMenuModule = {
|
|
6
|
+
Root: ComponentType<any>
|
|
7
|
+
Trigger: ComponentType<any>
|
|
8
|
+
Content: ComponentType<any>
|
|
9
|
+
Item: ComponentType<any>
|
|
10
|
+
ItemTitle: ComponentType<any>
|
|
11
|
+
ItemSubtitle: ComponentType<any>
|
|
12
|
+
ItemIcon: ComponentType<any>
|
|
13
|
+
ItemImage: ComponentType<any>
|
|
14
|
+
ItemIndicator: ComponentType<any>
|
|
15
|
+
Group: ComponentType<any>
|
|
16
|
+
Label: ComponentType<any>
|
|
17
|
+
Separator: ComponentType<any>
|
|
18
|
+
Sub: ComponentType<any>
|
|
19
|
+
SubTrigger: ComponentType<any>
|
|
20
|
+
SubContent: ComponentType<any>
|
|
21
|
+
CheckboxItem: ComponentType<any>
|
|
22
|
+
Preview?: ComponentType<any>
|
|
23
|
+
Auxiliary?: ComponentType<any>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A native implementation for Tamagui Menu and ContextMenu compound components.
|
|
28
|
+
* Register one adapter before the first native menu renders.
|
|
29
|
+
*/
|
|
30
|
+
export type NativeMenuAdapter = {
|
|
31
|
+
name: string
|
|
32
|
+
Menu: NativeMenuModule
|
|
33
|
+
ContextMenu: NativeMenuModule
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type NativeMenuState = {
|
|
37
|
+
enabled: boolean
|
|
38
|
+
adapter: NativeMenuAdapter | null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const state = createGlobalState<NativeMenuState>('native-menu-adapter', {
|
|
42
|
+
enabled: false,
|
|
43
|
+
adapter: null,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Registers the process-wide native menu implementation. Re-registering the same
|
|
48
|
+
* named adapter supports fast refresh; registering a different adapter throws.
|
|
49
|
+
*/
|
|
50
|
+
export function registerNativeMenuAdapter(adapter: NativeMenuAdapter): void {
|
|
51
|
+
const current = state.get().adapter
|
|
52
|
+
if (current && current.name !== adapter.name) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`A native menu adapter named "${current.name}" is already registered. ` +
|
|
55
|
+
`Register exactly one adapter before rendering Tamagui menus.`
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
state.set({ enabled: true, adapter })
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function getNativeMenuAdapter(): NativeMenuAdapter | null {
|
|
62
|
+
return state.get().adapter
|
|
63
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup Expo UI for Tamagui native Menu and ContextMenu components.
|
|
3
|
+
*
|
|
4
|
+
* Import this module once at the top of the app entry point after installing
|
|
5
|
+
* `@expo/ui`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createExpoUIMenuAdapter } from './expoUIMenuAdapter'
|
|
9
|
+
import { registerNativeMenuAdapter } from './nativeMenuState'
|
|
10
|
+
|
|
11
|
+
function setup(): void {
|
|
12
|
+
let MenuView
|
|
13
|
+
try {
|
|
14
|
+
MenuView = require('@expo/ui/community/menu').MenuView
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (process.env.NODE_ENV === 'development') {
|
|
17
|
+
console.warn('Error setting up Expo UI native menus', error)
|
|
18
|
+
}
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
registerNativeMenuAdapter(createExpoUIMenuAdapter({ MenuView }))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setup()
|
package/src/setup-zeego.ts
CHANGED
|
@@ -12,30 +12,26 @@
|
|
|
12
12
|
* Menu and ContextMenu native mode.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { registerNativeMenuAdapter } from './nativeMenuState'
|
|
16
16
|
|
|
17
17
|
function setup(): void {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
g.__tamagui_native_zeego_setup = true
|
|
21
|
-
|
|
18
|
+
let Menu
|
|
19
|
+
let ContextMenu
|
|
22
20
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (DropdownMenu && ContextMenu) {
|
|
27
|
-
getZeego().set({
|
|
28
|
-
enabled: true,
|
|
29
|
-
DropdownMenu,
|
|
30
|
-
ContextMenu,
|
|
31
|
-
})
|
|
32
|
-
}
|
|
21
|
+
Menu = require('zeego/dropdown-menu')
|
|
22
|
+
ContextMenu = require('zeego/context-menu')
|
|
33
23
|
} catch (err) {
|
|
34
24
|
if (process.env.NODE_ENV === 'development') {
|
|
35
25
|
console.warn(`Error setting up Zeego`, err)
|
|
36
26
|
}
|
|
37
|
-
|
|
27
|
+
return
|
|
38
28
|
}
|
|
29
|
+
|
|
30
|
+
registerNativeMenuAdapter({
|
|
31
|
+
name: 'zeego',
|
|
32
|
+
Menu,
|
|
33
|
+
ContextMenu,
|
|
34
|
+
})
|
|
39
35
|
}
|
|
40
36
|
|
|
41
37
|
// run setup immediately on import
|
package/src/types.ts
CHANGED
|
@@ -68,12 +68,6 @@ export interface LinearGradientState {
|
|
|
68
68
|
Component: any
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
export interface ZeegoState {
|
|
72
|
-
enabled: boolean
|
|
73
|
-
DropdownMenu: any
|
|
74
|
-
ContextMenu: any
|
|
75
|
-
}
|
|
76
|
-
|
|
77
71
|
export interface BurntState {
|
|
78
72
|
enabled: boolean
|
|
79
73
|
toast: ((options: any) => void) | null
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ComponentType } from "react";
|
|
2
|
+
import type { NativeMenuAdapter } from "./nativeMenuState";
|
|
3
|
+
export type ExpoUIMenuAdapterOptions = {
|
|
4
|
+
MenuView: ComponentType<any>;
|
|
5
|
+
};
|
|
6
|
+
export declare function createExpoUIMenuAdapter({ MenuView }: ExpoUIMenuAdapterOptions): NativeMenuAdapter;
|
|
7
|
+
|
|
8
|
+
//# sourceMappingURL=expoUIMenuAdapter.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mappings": "AAAA,cAAc,qBAA8C;AAG5D,cAAc,yBAA2C;AA0BzD,YAAY,2BAA2B;CACrC,UAAU;;AAgCZ,OAAO,iBAAS,wBAAwB,EACtC,YACC,2BAA2B",
|
|
3
|
+
"names": [],
|
|
4
|
+
"sources": [
|
|
5
|
+
"src/expoUIMenuAdapter.tsx"
|
|
6
|
+
],
|
|
7
|
+
"version": 3,
|
|
8
|
+
"sourcesContent": [
|
|
9
|
+
"import type { ComponentType, ReactElement, ReactNode } from 'react'\nimport React from 'react'\n\nimport type { NativeMenuAdapter, NativeMenuModule } from './nativeMenuState'\n\ntype ExpoMenuAction = {\n id: string\n title: string\n image?: unknown\n state?: 'on' | 'off'\n attributes?: {\n destructive?: boolean\n disabled?: boolean\n hidden?: boolean\n }\n subactions?: ExpoMenuAction[]\n displayInline?: boolean\n}\n\ntype ExpoMenuViewProps = {\n actions: ExpoMenuAction[]\n title?: string\n shouldOpenOnLongPress?: boolean\n onPressAction?: (event: { nativeEvent: { event: string } }) => void\n onOpenMenu?: () => void\n onCloseMenu?: () => void\n children?: ReactNode\n}\n\nexport type ExpoUIMenuAdapterOptions = {\n MenuView: ComponentType<any>\n}\n\ntype MarkerProps = {\n children?: ReactNode\n [key: string]: unknown\n}\n\nfunction marker(name: string): ComponentType<MarkerProps> {\n const Marker = () => null\n Marker.displayName = `ExpoUI${name}`\n return Marker\n}\n\nfunction elementOfType(\n children: ReactNode,\n type: ComponentType<MarkerProps>\n): ReactElement<MarkerProps> | null {\n for (const child of React.Children.toArray(children)) {\n if (React.isValidElement<MarkerProps>(child) && child.type === type) {\n return child\n }\n }\n return null\n}\n\nfunction textFromNode(node: ReactNode): string {\n if (typeof node === 'string' || typeof node === 'number') return String(node)\n if (!React.isValidElement<MarkerProps>(node)) return ''\n return React.Children.toArray(node.props.children).map(textFromNode).join('')\n}\n\nexport function createExpoUIMenuAdapter({\n MenuView,\n}: ExpoUIMenuAdapterOptions): NativeMenuAdapter {\n const NativeMenuView: ComponentType<ExpoMenuViewProps> = MenuView\n\n function createModule(isContextMenu: boolean): NativeMenuModule {\n const Trigger = marker('Trigger')\n const Content = marker('Content')\n const Item = marker('Item')\n const ItemTitle = marker('ItemTitle')\n const ItemSubtitle = marker('ItemSubtitle')\n const ItemIcon = marker('ItemIcon')\n const ItemImage = marker('ItemImage')\n const ItemIndicator = marker('ItemIndicator')\n const Group = marker('Group')\n const Label = marker('Label')\n const Separator = marker('Separator')\n const Sub = marker('Sub')\n const SubTrigger = marker('SubTrigger')\n const SubContent = marker('SubContent')\n const CheckboxItem = marker('CheckboxItem')\n const Preview = marker('Preview')\n const Auxiliary = marker('Auxiliary')\n\n function Root({\n children,\n onOpenChange,\n onOpenWillChange,\n }: {\n children?: ReactNode\n onOpenChange?: (open: boolean) => void\n onOpenWillChange?: (open: boolean) => void\n }) {\n const trigger = elementOfType(children, Trigger)\n const content = elementOfType(children, Content)\n const preview = elementOfType(children, Preview)\n const auxiliary = elementOfType(children, Auxiliary)\n\n if (preview || auxiliary) {\n throw new Error(\n '@expo/ui/community/menu does not support Tamagui context-menu previews or auxiliary views'\n )\n }\n if (!trigger || !content) {\n throw new Error('Tamagui native menus require one Trigger and one Content')\n }\n\n let nextId = 0\n const handlers = new Map<string, () => void>()\n\n function actionId(element: ReactElement<MarkerProps>): string {\n return element.key == null\n ? `tamagui-menu-${nextId++}`\n : String(element.key).replace(/^\\.\\$/, '')\n }\n\n function itemTitle(element: ReactElement<MarkerProps>): string {\n if (typeof element.props.textValue === 'string') return element.props.textValue\n const title = elementOfType(element.props.children, ItemTitle)\n return title ? textFromNode(title.props.children) : ''\n }\n\n function actionImage(element: ReactElement<MarkerProps>): unknown {\n const image = elementOfType(element.props.children, ItemImage)\n if (image?.props.source) return image.props.source\n const icon = elementOfType(element.props.children, ItemIcon)\n if (!icon) return undefined\n const ios = icon.props.ios\n if (\n typeof ios === 'object' &&\n ios &&\n 'name' in ios &&\n typeof ios.name === 'string'\n ) {\n return ios.name\n }\n return typeof icon.props.iosIconName === 'string'\n ? icon.props.iosIconName\n : undefined\n }\n\n function attributes(\n element: ReactElement<MarkerProps>\n ): ExpoMenuAction['attributes'] {\n const destructive = element.props.destructive === true\n const disabled = element.props.disabled === true\n const hidden = element.props.hidden === true\n return destructive || disabled || hidden\n ? { destructive, disabled, hidden }\n : undefined\n }\n\n function actionsFrom(childrenToParse: ReactNode): ExpoMenuAction[] {\n const actions: ExpoMenuAction[] = []\n let section: ExpoMenuAction[] | null = null\n\n function append(action: ExpoMenuAction) {\n if (section) {\n section.push(action)\n } else {\n actions.push(action)\n }\n }\n\n function flushSection() {\n if (!section?.length) {\n section = []\n return\n }\n actions.push({\n id: `tamagui-menu-section-${nextId++}`,\n title: '',\n subactions: section,\n displayInline: true,\n })\n section = []\n }\n\n for (const child of React.Children.toArray(childrenToParse)) {\n if (!React.isValidElement<MarkerProps>(child)) continue\n\n if (child.type === Separator) {\n if (!section) section = actions.splice(0)\n flushSection()\n continue\n }\n\n if (child.type === Group) {\n const label = elementOfType(child.props.children, Label)\n append({\n id: actionId(child),\n title: label ? textFromNode(label.props.children) : '',\n subactions: actionsFrom(child.props.children),\n displayInline: true,\n })\n continue\n }\n\n if (child.type === Sub) {\n const subTrigger = elementOfType(child.props.children, SubTrigger)\n const subContent = elementOfType(child.props.children, SubContent)\n if (!subTrigger || !subContent) {\n throw new Error(\n 'Tamagui native submenus require one SubTrigger and SubContent'\n )\n }\n append({\n id: actionId(child),\n title: itemTitle(subTrigger),\n image: actionImage(subTrigger),\n attributes: attributes(subTrigger),\n subactions: actionsFrom(subContent.props.children),\n })\n continue\n }\n\n if (child.type !== Item && child.type !== CheckboxItem) continue\n\n if (elementOfType(child.props.children, ItemSubtitle)) {\n throw new Error(\n '@expo/ui/community/menu does not support menu item subtitles'\n )\n }\n\n const id = actionId(child)\n if (child.type === CheckboxItem) {\n const current =\n child.props.value === true ||\n child.props.value === 'on' ||\n child.props.checked === true\n const onValueChange = child.props.onValueChange\n const onCheckedChange = child.props.onCheckedChange\n handlers.set(id, () => {\n if (typeof onValueChange === 'function') {\n onValueChange(current ? 'off' : 'on', current ? 'on' : 'off')\n } else if (typeof onCheckedChange === 'function') {\n onCheckedChange(!current)\n }\n })\n append({\n id,\n title: itemTitle(child),\n image: actionImage(child),\n state: current ? 'on' : 'off',\n attributes: attributes(child),\n })\n continue\n }\n\n const onSelect = child.props.onSelect\n if (typeof onSelect === 'function') {\n handlers.set(id, () => onSelect())\n }\n append({\n id,\n title: itemTitle(child),\n image: actionImage(child),\n attributes: attributes(child),\n })\n }\n\n if (section) flushSection()\n return actions\n }\n\n const label = elementOfType(content.props.children, Label)\n const triggerAction = trigger.props.action\n\n return (\n <NativeMenuView\n actions={actionsFrom(content.props.children)}\n title={label ? textFromNode(label.props.children) : undefined}\n shouldOpenOnLongPress={\n triggerAction === 'longPress' || (triggerAction !== 'press' && isContextMenu)\n }\n onPressAction={(event) => handlers.get(event.nativeEvent.event)?.()}\n onOpenMenu={() => {\n onOpenWillChange?.(true)\n onOpenChange?.(true)\n }}\n onCloseMenu={() => {\n onOpenWillChange?.(false)\n onOpenChange?.(false)\n }}\n >\n {trigger.props.children}\n </NativeMenuView>\n )\n }\n\n return {\n Root,\n Trigger,\n Content,\n Item,\n ItemTitle,\n ItemSubtitle,\n ItemIcon,\n ItemImage,\n ItemIndicator,\n Group,\n Label,\n Separator,\n Sub,\n SubTrigger,\n SubContent,\n CheckboxItem,\n Preview,\n Auxiliary,\n }\n }\n\n return {\n name: 'expo-ui',\n Menu: createModule(false),\n ContextMenu: createModule(true),\n }\n}\n"
|
|
10
|
+
]
|
|
11
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* // LinearGradient will use expo-linear-gradient when installed
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
|
-
export type { NativePortalState, GestureState, WorkletsState, SafeAreaState, SafeAreaInsets, SafeAreaFrame, SafeAreaMetrics, LinearGradientState,
|
|
21
|
+
export type { NativePortalState, GestureState, WorkletsState, SafeAreaState, SafeAreaInsets, SafeAreaFrame, SafeAreaMetrics, LinearGradientState, BurntState, NativePortalProps, NativePortalHostProps, NativePortalProviderProps } from "./types";
|
|
22
|
+
export type { NativeMenuAdapter, NativeMenuModule } from "./nativeMenuState";
|
|
22
23
|
export { getPortal } from "./portalState";
|
|
23
24
|
export type { PortalAccessor } from "./portalState";
|
|
24
25
|
export { getGestureHandler } from "./gestureState";
|
|
@@ -33,8 +34,9 @@ export { getLinearGradient } from "./linearGradientState";
|
|
|
33
34
|
export type { LinearGradientAccessor } from "./linearGradientState";
|
|
34
35
|
export { isKeyboardControllerEnabled, getKeyboardControllerState, setKeyboardControllerState } from "./keyboardControllerState";
|
|
35
36
|
export type { KeyboardControllerState } from "./keyboardControllerState";
|
|
36
|
-
export {
|
|
37
|
-
export
|
|
37
|
+
export { getNativeMenuAdapter, registerNativeMenuAdapter } from "./nativeMenuState";
|
|
38
|
+
export { createExpoUIMenuAdapter } from "./expoUIMenuAdapter";
|
|
39
|
+
export type { ExpoUIMenuAdapterOptions } from "./expoUIMenuAdapter";
|
|
38
40
|
export { NativeMenuContext } from "./nativeMenuContext";
|
|
39
41
|
export { getBurnt } from "./burntState";
|
|
40
42
|
export type { BurntAccessor } from "./burntState";
|
package/types/index.d.ts.map
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAsBA,cACE,mBACA,cACA,eACA,eACA,gBACA,eACA,iBACA,qBACA,YACA,
|
|
2
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAsBA,cACE,mBACA,cACA,eACA,eACA,gBACA,eACA,iBACA,qBACA,YACA,mBACA,uBACA,iCACK;AACP,cAAc,mBAAmB,wBAAwB;AAGzD,SAAS,iBAAiB;AAC1B,cAAc,sBAAsB;AAGpC,SAAS,yBAAyB;AAClC,SACE,+BAA+B,sCAC/B,6BAA6B,oCAC7B,iCAAiC,8CAC5B;AACP,cACE,6BACA,wBACA,0BACK;AAGP,cAAc,4BAA4B;AAG1C,SAAS,mBAAmB;AAC5B,cAAc,wBAAwB;AAGtC,SAAS,mBAAmB;AAC5B,cAAc,wBAAwB;AAGtC,SAAS,yBAAyB;AAClC,cAAc,8BAA8B;AAG5C,SACE,6BACA,4BACA,kCACK;AACP,cAAc,+BAA+B;AAG7C,SAAS,sBAAsB,iCAAiC;AAChE,SAAS,+BAA+B;AACxC,cAAc,gCAAgC;AAC9C,SAAS,yBAAyB;AAGlC,SAAS,gBAAgB;AACzB,cAAc,qBAAqB;AAGnC,SAAS,cAAc,kBAAkB,4BAA4B",
|
|
3
3
|
"names": [],
|
|
4
4
|
"sources": [
|
|
5
5
|
"src/index.ts"
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"/**\n * @tamagui/native\n *\n * Native setup modules for Tamagui. Import these at the top of your app entry point.\n *\n * @example\n * ```tsx\n * // In your app entry (index.js or App.tsx)\n * import '@tamagui/native/setup-teleport'\n * import '@tamagui/native/setup-gesture-handler'\n * import '@tamagui/native/setup-worklets'\n * import '@tamagui/native/setup-safe-area'\n * import '@tamagui/native/expo-linear-gradient'\n * import '@tamagui/native/setup-keyboard-controller'\n *\n * // Then use Tamagui components normally\n * // Sheet will automatically use native gestures when available\n * // LinearGradient will use expo-linear-gradient when installed\n * ```\n */\n\n// types\nexport type {\n NativePortalState,\n GestureState,\n WorkletsState,\n SafeAreaState,\n SafeAreaInsets,\n SafeAreaFrame,\n SafeAreaMetrics,\n LinearGradientState,\n
|
|
9
|
+
"/**\n * @tamagui/native\n *\n * Native setup modules for Tamagui. Import these at the top of your app entry point.\n *\n * @example\n * ```tsx\n * // In your app entry (index.js or App.tsx)\n * import '@tamagui/native/setup-teleport'\n * import '@tamagui/native/setup-gesture-handler'\n * import '@tamagui/native/setup-worklets'\n * import '@tamagui/native/setup-safe-area'\n * import '@tamagui/native/expo-linear-gradient'\n * import '@tamagui/native/setup-keyboard-controller'\n *\n * // Then use Tamagui components normally\n * // Sheet will automatically use native gestures when available\n * // LinearGradient will use expo-linear-gradient when installed\n * ```\n */\n\n// types\nexport type {\n NativePortalState,\n GestureState,\n WorkletsState,\n SafeAreaState,\n SafeAreaInsets,\n SafeAreaFrame,\n SafeAreaMetrics,\n LinearGradientState,\n BurntState,\n NativePortalProps,\n NativePortalHostProps,\n NativePortalProviderProps,\n} from './types'\nexport type { NativeMenuAdapter, NativeMenuModule } from './nativeMenuState'\n\n// portal\nexport { getPortal } from './portalState'\nexport type { PortalAccessor } from './portalState'\n\n// gesture handler\nexport { getGestureHandler } from './gestureState'\nexport {\n claimExternalPressOwnership as unstable_claimExternalPressOwnership,\n hasExternalPressOwnership as unstable_hasExternalPressOwnership,\n releaseExternalPressOwnership as unstable_releaseExternalPressOwnership,\n} from './gestureState'\nexport type {\n ExternalPressOwnershipToken,\n GestureHandlerAccessor,\n PressGestureConfig,\n} from './gestureState'\n// NOTE: setupGestureHandler is exported from setup-gesture-handler.ts entry point,\n// not here, to avoid bundler pulling in RNGH require during tree-shaking\nexport type { GestureHandlerConfig } from './setup-gesture-handler'\n\n// worklets\nexport { getWorklets } from './workletsState'\nexport type { WorkletsAccessor } from './workletsState'\n\n// safe area\nexport { getSafeArea } from './safeAreaState'\nexport type { SafeAreaAccessor } from './safeAreaState'\n\n// linear gradient\nexport { getLinearGradient } from './linearGradientState'\nexport type { LinearGradientAccessor } from './linearGradientState'\n\n// keyboard controller state exports (safe - no side effects)\nexport {\n isKeyboardControllerEnabled,\n getKeyboardControllerState,\n setKeyboardControllerState,\n} from './keyboardControllerState'\nexport type { KeyboardControllerState } from './keyboardControllerState'\n\n// native menus\nexport { getNativeMenuAdapter, registerNativeMenuAdapter } from './nativeMenuState'\nexport { createExpoUIMenuAdapter } from './expoUIMenuAdapter'\nexport type { ExpoUIMenuAdapterOptions } from './expoUIMenuAdapter'\nexport { NativeMenuContext } from './nativeMenuContext'\n\n// burnt (native toasts)\nexport { getBurnt } from './burntState'\nexport type { BurntAccessor } from './burntState'\n\n// components\nexport { NativePortal, NativePortalHost, NativePortalProvider } from './components'\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ComponentType } from "react";
|
|
2
|
+
export type NativeMenuModule = {
|
|
3
|
+
Root: ComponentType<any>;
|
|
4
|
+
Trigger: ComponentType<any>;
|
|
5
|
+
Content: ComponentType<any>;
|
|
6
|
+
Item: ComponentType<any>;
|
|
7
|
+
ItemTitle: ComponentType<any>;
|
|
8
|
+
ItemSubtitle: ComponentType<any>;
|
|
9
|
+
ItemIcon: ComponentType<any>;
|
|
10
|
+
ItemImage: ComponentType<any>;
|
|
11
|
+
ItemIndicator: ComponentType<any>;
|
|
12
|
+
Group: ComponentType<any>;
|
|
13
|
+
Label: ComponentType<any>;
|
|
14
|
+
Separator: ComponentType<any>;
|
|
15
|
+
Sub: ComponentType<any>;
|
|
16
|
+
SubTrigger: ComponentType<any>;
|
|
17
|
+
SubContent: ComponentType<any>;
|
|
18
|
+
CheckboxItem: ComponentType<any>;
|
|
19
|
+
Preview?: ComponentType<any>;
|
|
20
|
+
Auxiliary?: ComponentType<any>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* A native implementation for Tamagui Menu and ContextMenu compound components.
|
|
24
|
+
* Register one adapter before the first native menu renders.
|
|
25
|
+
*/
|
|
26
|
+
export type NativeMenuAdapter = {
|
|
27
|
+
name: string;
|
|
28
|
+
Menu: NativeMenuModule;
|
|
29
|
+
ContextMenu: NativeMenuModule;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Registers the process-wide native menu implementation. Re-registering the same
|
|
33
|
+
* named adapter supports fast refresh; registering a different adapter throws.
|
|
34
|
+
*/
|
|
35
|
+
export declare function registerNativeMenuAdapter(adapter: NativeMenuAdapter): void;
|
|
36
|
+
export declare function getNativeMenuAdapter(): NativeMenuAdapter | null;
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=nativeMenuState.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mappings": "AAAA,cAAc,qBAAqB;AAInC,YAAY,mBAAmB;CAC7B,MAAM;CACN,SAAS;CACT,SAAS;CACT,MAAM;CACN,WAAW;CACX,cAAc;CACd,UAAU;CACV,WAAW;CACX,eAAe;CACf,OAAO;CACP,OAAO;CACP,WAAW;CACX,KAAK;CACL,YAAY;CACZ,YAAY;CACZ,cAAc;CACd,UAAU;CACV,YAAY;;;;;;AAOd,YAAY,oBAAoB;CAC9B;CACA,MAAM;CACN,aAAa;;;;;;AAiBf,OAAO,iBAAS,0BAA0B,SAAS;AAWnD,OAAO,iBAAS,wBAAwB",
|
|
3
|
+
"names": [],
|
|
4
|
+
"sources": [
|
|
5
|
+
"src/nativeMenuState.ts"
|
|
6
|
+
],
|
|
7
|
+
"version": 3,
|
|
8
|
+
"sourcesContent": [
|
|
9
|
+
"import type { ComponentType } from 'react'\n\nimport { createGlobalState } from './globalState'\n\nexport type NativeMenuModule = {\n Root: ComponentType<any>\n Trigger: ComponentType<any>\n Content: ComponentType<any>\n Item: ComponentType<any>\n ItemTitle: ComponentType<any>\n ItemSubtitle: ComponentType<any>\n ItemIcon: ComponentType<any>\n ItemImage: ComponentType<any>\n ItemIndicator: ComponentType<any>\n Group: ComponentType<any>\n Label: ComponentType<any>\n Separator: ComponentType<any>\n Sub: ComponentType<any>\n SubTrigger: ComponentType<any>\n SubContent: ComponentType<any>\n CheckboxItem: ComponentType<any>\n Preview?: ComponentType<any>\n Auxiliary?: ComponentType<any>\n}\n\n/**\n * A native implementation for Tamagui Menu and ContextMenu compound components.\n * Register one adapter before the first native menu renders.\n */\nexport type NativeMenuAdapter = {\n name: string\n Menu: NativeMenuModule\n ContextMenu: NativeMenuModule\n}\n\ntype NativeMenuState = {\n enabled: boolean\n adapter: NativeMenuAdapter | null\n}\n\nconst state = createGlobalState<NativeMenuState>('native-menu-adapter', {\n enabled: false,\n adapter: null,\n})\n\n/**\n * Registers the process-wide native menu implementation. Re-registering the same\n * named adapter supports fast refresh; registering a different adapter throws.\n */\nexport function registerNativeMenuAdapter(adapter: NativeMenuAdapter): void {\n const current = state.get().adapter\n if (current && current.name !== adapter.name) {\n throw new Error(\n `A native menu adapter named \"${current.name}\" is already registered. ` +\n `Register exactly one adapter before rendering Tamagui menus.`\n )\n }\n state.set({ enabled: true, adapter })\n}\n\nexport function getNativeMenuAdapter(): NativeMenuAdapter | null {\n return state.get().adapter\n}\n"
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mappings": "",
|
|
3
|
+
"names": [],
|
|
4
|
+
"sources": [
|
|
5
|
+
"src/setup-expo-ui-menu.ts"
|
|
6
|
+
],
|
|
7
|
+
"version": 3,
|
|
8
|
+
"sourcesContent": [
|
|
9
|
+
"/**\n * Setup Expo UI for Tamagui native Menu and ContextMenu components.\n *\n * Import this module once at the top of the app entry point after installing\n * `@expo/ui`.\n */\n\nimport { createExpoUIMenuAdapter } from './expoUIMenuAdapter'\nimport { registerNativeMenuAdapter } from './nativeMenuState'\n\nfunction setup(): void {\n let MenuView\n try {\n MenuView = require('@expo/ui/community/menu').MenuView\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.warn('Error setting up Expo UI native menus', error)\n }\n return\n }\n\n registerNativeMenuAdapter(createExpoUIMenuAdapter({ MenuView }))\n}\n\nsetup()\n"
|
|
10
|
+
]
|
|
11
|
+
}
|