@tamagui/create-menu 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/MenuPredefined.cjs +189 -0
  3. package/dist/cjs/MenuPredefined.js +168 -0
  4. package/dist/cjs/MenuPredefined.js.map +6 -0
  5. package/dist/cjs/MenuPredefined.native.js +192 -0
  6. package/dist/cjs/MenuPredefined.native.js.map +1 -0
  7. package/dist/cjs/createBaseMenu.cjs +1147 -0
  8. package/dist/cjs/createBaseMenu.js +884 -0
  9. package/dist/cjs/createBaseMenu.js.map +6 -0
  10. package/dist/cjs/createBaseMenu.native.js +1346 -0
  11. package/dist/cjs/createBaseMenu.native.js.map +1 -0
  12. package/dist/cjs/createNativeMenu/createNativeMenu.cjs +242 -0
  13. package/dist/cjs/createNativeMenu/createNativeMenu.js +184 -0
  14. package/dist/cjs/createNativeMenu/createNativeMenu.js.map +6 -0
  15. package/dist/cjs/createNativeMenu/createNativeMenu.native.js +307 -0
  16. package/dist/cjs/createNativeMenu/createNativeMenu.native.js.map +1 -0
  17. package/dist/cjs/createNativeMenu/createNativeMenuTypes.cjs +16 -0
  18. package/dist/cjs/createNativeMenu/createNativeMenuTypes.js +14 -0
  19. package/dist/cjs/createNativeMenu/createNativeMenuTypes.js.map +6 -0
  20. package/dist/cjs/createNativeMenu/createNativeMenuTypes.native.js +19 -0
  21. package/dist/cjs/createNativeMenu/createNativeMenuTypes.native.js.map +1 -0
  22. package/dist/cjs/createNativeMenu/utils.cjs +68 -0
  23. package/dist/cjs/createNativeMenu/utils.js +66 -0
  24. package/dist/cjs/createNativeMenu/utils.js.map +6 -0
  25. package/dist/cjs/createNativeMenu/utils.native.js +94 -0
  26. package/dist/cjs/createNativeMenu/utils.native.js.map +1 -0
  27. package/dist/cjs/createNativeMenu/withNativeMenu.cjs +37 -0
  28. package/dist/cjs/createNativeMenu/withNativeMenu.js +30 -0
  29. package/dist/cjs/createNativeMenu/withNativeMenu.js.map +6 -0
  30. package/dist/cjs/createNativeMenu/withNativeMenu.native.js +43 -0
  31. package/dist/cjs/createNativeMenu/withNativeMenu.native.js.map +1 -0
  32. package/dist/cjs/index.cjs +30 -0
  33. package/dist/cjs/index.js +24 -0
  34. package/dist/cjs/index.js.map +6 -0
  35. package/dist/cjs/index.native.js +33 -0
  36. package/dist/cjs/index.native.js.map +1 -0
  37. package/dist/esm/MenuPredefined.js +154 -0
  38. package/dist/esm/MenuPredefined.js.map +6 -0
  39. package/dist/esm/MenuPredefined.mjs +166 -0
  40. package/dist/esm/MenuPredefined.mjs.map +1 -0
  41. package/dist/esm/MenuPredefined.native.js +166 -0
  42. package/dist/esm/MenuPredefined.native.js.map +1 -0
  43. package/dist/esm/createBaseMenu.js +892 -0
  44. package/dist/esm/createBaseMenu.js.map +6 -0
  45. package/dist/esm/createBaseMenu.mjs +1113 -0
  46. package/dist/esm/createBaseMenu.mjs.map +1 -0
  47. package/dist/esm/createBaseMenu.native.js +1309 -0
  48. package/dist/esm/createBaseMenu.native.js.map +1 -0
  49. package/dist/esm/createNativeMenu/createNativeMenu.js +163 -0
  50. package/dist/esm/createNativeMenu/createNativeMenu.js.map +6 -0
  51. package/dist/esm/createNativeMenu/createNativeMenu.mjs +208 -0
  52. package/dist/esm/createNativeMenu/createNativeMenu.mjs.map +1 -0
  53. package/dist/esm/createNativeMenu/createNativeMenu.native.js +270 -0
  54. package/dist/esm/createNativeMenu/createNativeMenu.native.js.map +1 -0
  55. package/dist/esm/createNativeMenu/createNativeMenuTypes.js +1 -0
  56. package/dist/esm/createNativeMenu/createNativeMenuTypes.js.map +6 -0
  57. package/dist/esm/createNativeMenu/createNativeMenuTypes.mjs +2 -0
  58. package/dist/esm/createNativeMenu/createNativeMenuTypes.mjs.map +1 -0
  59. package/dist/esm/createNativeMenu/createNativeMenuTypes.native.js +2 -0
  60. package/dist/esm/createNativeMenu/createNativeMenuTypes.native.js.map +1 -0
  61. package/dist/esm/createNativeMenu/utils.js +47 -0
  62. package/dist/esm/createNativeMenu/utils.js.map +6 -0
  63. package/dist/esm/createNativeMenu/utils.mjs +29 -0
  64. package/dist/esm/createNativeMenu/utils.mjs.map +1 -0
  65. package/dist/esm/createNativeMenu/utils.native.js +52 -0
  66. package/dist/esm/createNativeMenu/utils.native.js.map +1 -0
  67. package/dist/esm/createNativeMenu/withNativeMenu.js +15 -0
  68. package/dist/esm/createNativeMenu/withNativeMenu.js.map +6 -0
  69. package/dist/esm/createNativeMenu/withNativeMenu.mjs +14 -0
  70. package/dist/esm/createNativeMenu/withNativeMenu.mjs.map +1 -0
  71. package/dist/esm/createNativeMenu/withNativeMenu.native.js +17 -0
  72. package/dist/esm/createNativeMenu/withNativeMenu.native.js.map +1 -0
  73. package/dist/esm/index.js +8 -0
  74. package/dist/esm/index.js.map +6 -0
  75. package/dist/esm/index.mjs +6 -0
  76. package/dist/esm/index.mjs.map +1 -0
  77. package/dist/esm/index.native.js +6 -0
  78. package/dist/esm/index.native.js.map +1 -0
  79. package/dist/jsx/MenuPredefined.js +154 -0
  80. package/dist/jsx/MenuPredefined.js.map +6 -0
  81. package/dist/jsx/MenuPredefined.mjs +166 -0
  82. package/dist/jsx/MenuPredefined.mjs.map +1 -0
  83. package/dist/jsx/MenuPredefined.native.js +192 -0
  84. package/dist/jsx/MenuPredefined.native.js.map +1 -0
  85. package/dist/jsx/createBaseMenu.js +892 -0
  86. package/dist/jsx/createBaseMenu.js.map +6 -0
  87. package/dist/jsx/createBaseMenu.mjs +1113 -0
  88. package/dist/jsx/createBaseMenu.mjs.map +1 -0
  89. package/dist/jsx/createBaseMenu.native.js +1346 -0
  90. package/dist/jsx/createBaseMenu.native.js.map +1 -0
  91. package/dist/jsx/createNativeMenu/createNativeMenu.js +163 -0
  92. package/dist/jsx/createNativeMenu/createNativeMenu.js.map +6 -0
  93. package/dist/jsx/createNativeMenu/createNativeMenu.mjs +208 -0
  94. package/dist/jsx/createNativeMenu/createNativeMenu.mjs.map +1 -0
  95. package/dist/jsx/createNativeMenu/createNativeMenu.native.js +307 -0
  96. package/dist/jsx/createNativeMenu/createNativeMenu.native.js.map +1 -0
  97. package/dist/jsx/createNativeMenu/createNativeMenuTypes.js +1 -0
  98. package/dist/jsx/createNativeMenu/createNativeMenuTypes.js.map +6 -0
  99. package/dist/jsx/createNativeMenu/createNativeMenuTypes.mjs +2 -0
  100. package/dist/jsx/createNativeMenu/createNativeMenuTypes.mjs.map +1 -0
  101. package/dist/jsx/createNativeMenu/createNativeMenuTypes.native.js +19 -0
  102. package/dist/jsx/createNativeMenu/createNativeMenuTypes.native.js.map +1 -0
  103. package/dist/jsx/createNativeMenu/utils.js +47 -0
  104. package/dist/jsx/createNativeMenu/utils.js.map +6 -0
  105. package/dist/jsx/createNativeMenu/utils.mjs +29 -0
  106. package/dist/jsx/createNativeMenu/utils.mjs.map +1 -0
  107. package/dist/jsx/createNativeMenu/utils.native.js +94 -0
  108. package/dist/jsx/createNativeMenu/utils.native.js.map +1 -0
  109. package/dist/jsx/createNativeMenu/withNativeMenu.js +15 -0
  110. package/dist/jsx/createNativeMenu/withNativeMenu.js.map +6 -0
  111. package/dist/jsx/createNativeMenu/withNativeMenu.mjs +14 -0
  112. package/dist/jsx/createNativeMenu/withNativeMenu.mjs.map +1 -0
  113. package/dist/jsx/createNativeMenu/withNativeMenu.native.js +43 -0
  114. package/dist/jsx/createNativeMenu/withNativeMenu.native.js.map +1 -0
  115. package/dist/jsx/index.js +8 -0
  116. package/dist/jsx/index.js.map +6 -0
  117. package/dist/jsx/index.mjs +6 -0
  118. package/dist/jsx/index.mjs.map +1 -0
  119. package/dist/jsx/index.native.js +33 -0
  120. package/dist/jsx/index.native.js.map +1 -0
  121. package/package.json +69 -0
  122. package/src/MenuPredefined.tsx +204 -0
  123. package/src/createBaseMenu.tsx +2031 -0
  124. package/src/createNativeMenu/createNativeMenu.tsx +400 -0
  125. package/src/createNativeMenu/createNativeMenuTypes.ts +214 -0
  126. package/src/createNativeMenu/utils.tsx +150 -0
  127. package/src/createNativeMenu/withNativeMenu.tsx +39 -0
  128. package/src/index.tsx +5 -0
  129. package/types/MenuPredefined.d.ts +31 -0
  130. package/types/MenuPredefined.d.ts.map +1 -0
  131. package/types/createBaseMenu.d.ts +298 -0
  132. package/types/createBaseMenu.d.ts.map +1 -0
  133. package/types/createNativeMenu/createNativeMenu.d.ts +42 -0
  134. package/types/createNativeMenu/createNativeMenu.d.ts.map +1 -0
  135. package/types/createNativeMenu/createNativeMenuTypes.d.ts +188 -0
  136. package/types/createNativeMenu/createNativeMenuTypes.d.ts.map +1 -0
  137. package/types/createNativeMenu/index.d.ts +4 -0
  138. package/types/createNativeMenu/utils.d.ts +38 -0
  139. package/types/createNativeMenu/utils.d.ts.map +1 -0
  140. package/types/createNativeMenu/withNativeMenu.d.ts +9 -0
  141. package/types/createNativeMenu/withNativeMenu.d.ts.map +1 -0
  142. package/types/index.d.ts +6 -0
  143. package/types/index.d.ts.map +1 -0
@@ -0,0 +1,1113 @@
1
+ import { Animate } from "@tamagui/animate";
2
+ import { AnimatePresence as Presence } from "@tamagui/animate-presence";
3
+ import { createCollection } from "@tamagui/collection";
4
+ import { Dismissable as DismissableLayer, dispatchDiscreteCustomEvent } from "@tamagui/dismissable";
5
+ import { useFocusGuards } from "@tamagui/focus-guard";
6
+ import { FocusScope } from "@tamagui/focus-scope";
7
+ import * as PopperPrimitive from "@tamagui/popper";
8
+ import { needsPortalRepropagation, Portal as PortalPrimitive } from "@tamagui/portal";
9
+ import { RemoveScroll } from "@tamagui/remove-scroll";
10
+ import { RovingFocusGroup } from "@tamagui/roving-focus";
11
+ import { useCallbackRef } from "@tamagui/use-callback-ref";
12
+ import { useDirection } from "@tamagui/use-direction";
13
+ import { composeEventHandlers, composeRefs, createStyledContext, isWeb, styled, Text, Theme, useComposedRefs, useIsTouchDevice, useThemeName, View, withStaticProperties } from "@tamagui/web";
14
+ import * as React from "react";
15
+ import { useId } from "react";
16
+ import { MenuPredefined } from "./MenuPredefined.mjs";
17
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
18
+ function whenMouse(handler) {
19
+ return event => event.pointerType === "mouse" ? handler(event) : void 0;
20
+ }
21
+ const SELECTION_KEYS = ["Enter", " "],
22
+ FIRST_KEYS = ["ArrowDown", "PageUp", "Home"],
23
+ LAST_KEYS = ["ArrowUp", "PageDown", "End"],
24
+ FIRST_LAST_KEYS = [...FIRST_KEYS, ...LAST_KEYS],
25
+ SUB_OPEN_KEYS = {
26
+ ltr: [...SELECTION_KEYS, "ArrowRight"],
27
+ rtl: [...SELECTION_KEYS, "ArrowLeft"]
28
+ },
29
+ SUB_CLOSE_KEYS = {
30
+ ltr: ["ArrowLeft"],
31
+ rtl: ["ArrowRight"]
32
+ },
33
+ MENU_NAME = "Menu",
34
+ [Collection, useCollection] = createCollection(MENU_NAME),
35
+ {
36
+ Provider: MenuProvider,
37
+ useStyledContext: useMenuContext
38
+ } = createStyledContext(),
39
+ {
40
+ Provider: MenuRootProvider,
41
+ useStyledContext: useMenuRootContext
42
+ } = createStyledContext(),
43
+ MENU_CONTEXT = "MenuContext";
44
+ function createBaseMenu({
45
+ Item: _Item = MenuPredefined.MenuItem,
46
+ Title: _Title = MenuPredefined.Title,
47
+ SubTitle: _SubTitle = MenuPredefined.SubTitle,
48
+ Image: _Image = MenuPredefined.MenuImage,
49
+ Icon: _Icon = MenuPredefined.MenuIcon,
50
+ Indicator: _Indicator = MenuPredefined.MenuIndicator,
51
+ Separator: _Separator = MenuPredefined.MenuSeparator,
52
+ MenuGroup: _MenuGroup = MenuPredefined.MenuGroup,
53
+ Label: _Label = MenuPredefined.MenuLabel
54
+ }) {
55
+ const MenuComp = props => {
56
+ const direction = useDirection(props.dir),
57
+ defaultPlacement = direction === "rtl" ? "bottom-end" : "bottom-start",
58
+ {
59
+ scope = MENU_CONTEXT,
60
+ open = !1,
61
+ children,
62
+ dir,
63
+ onOpenChange,
64
+ modal = !0,
65
+ allowFlip = {
66
+ padding: 10
67
+ },
68
+ stayInFrame = {
69
+ padding: 10
70
+ },
71
+ placement = defaultPlacement,
72
+ resize = !0,
73
+ offset = 10,
74
+ ...rest
75
+ } = props,
76
+ [content, setContent] = React.useState(null),
77
+ isUsingKeyboardRef = React.useRef(!1),
78
+ handleOpenChange = useCallbackRef(onOpenChange);
79
+ return isWeb && React.useEffect(() => {
80
+ const handleKeyDown = () => {
81
+ isUsingKeyboardRef.current = !0, document.addEventListener("pointerdown", handlePointer, {
82
+ capture: !0,
83
+ once: !0
84
+ }), document.addEventListener("pointermove", handlePointer, {
85
+ capture: !0,
86
+ once: !0
87
+ });
88
+ },
89
+ handlePointer = () => isUsingKeyboardRef.current = !1;
90
+ return document.addEventListener("keydown", handleKeyDown, {
91
+ capture: !0
92
+ }), () => {
93
+ document.removeEventListener("keydown", handleKeyDown, {
94
+ capture: !0
95
+ }), document.removeEventListener("pointerdown", handlePointer, {
96
+ capture: !0
97
+ }), document.removeEventListener("pointermove", handlePointer, {
98
+ capture: !0
99
+ });
100
+ };
101
+ }, []), /* @__PURE__ */jsx(PopperPrimitive.Popper, {
102
+ scope,
103
+ open,
104
+ placement,
105
+ allowFlip,
106
+ stayInFrame,
107
+ resize,
108
+ offset,
109
+ ...rest,
110
+ children: /* @__PURE__ */jsx(MenuProvider, {
111
+ scope,
112
+ open,
113
+ onOpenChange: handleOpenChange,
114
+ content,
115
+ onContentChange: setContent,
116
+ children: /* @__PURE__ */jsx(MenuRootProvider, {
117
+ scope,
118
+ open,
119
+ onClose: React.useCallback(() => handleOpenChange(!1), [handleOpenChange]),
120
+ isUsingKeyboardRef,
121
+ dir: direction,
122
+ modal,
123
+ children: /* @__PURE__ */jsx(MenuSubProvider, {
124
+ scope,
125
+ children
126
+ })
127
+ })
128
+ })
129
+ });
130
+ },
131
+ RepropagateMenuAndMenuRootProvider = props => {
132
+ const {
133
+ scope = MENU_CONTEXT,
134
+ menuContext,
135
+ rootContext,
136
+ popperContext,
137
+ menuSubContext,
138
+ children
139
+ } = props;
140
+ return /* @__PURE__ */jsx(PopperPrimitive.PopperProvider, {
141
+ ...popperContext,
142
+ scope,
143
+ children: /* @__PURE__ */jsx(MenuProvider, {
144
+ scope,
145
+ ...menuContext,
146
+ children: /* @__PURE__ */jsx(MenuRootProvider, {
147
+ scope,
148
+ ...rootContext,
149
+ children: menuSubContext ? /* @__PURE__ */jsx(MenuSubProvider, {
150
+ scope,
151
+ ...menuSubContext,
152
+ children
153
+ }) : children
154
+ })
155
+ })
156
+ });
157
+ };
158
+ MenuComp.displayName = MENU_NAME;
159
+ const ANCHOR_NAME = "MenuAnchor",
160
+ MenuAnchor = props => /* @__PURE__ */jsx(PopperPrimitive.PopperAnchor, {
161
+ scope: MENU_CONTEXT,
162
+ ...props
163
+ });
164
+ MenuAnchor.displayName = ANCHOR_NAME;
165
+ const PORTAL_NAME = "MenuPortal",
166
+ {
167
+ Provider: PortalProvider,
168
+ useStyledContext: usePortalContext
169
+ } = createStyledContext(void 0, "Portal"),
170
+ MenuPortal = props => {
171
+ const {
172
+ scope = MENU_CONTEXT,
173
+ forceMount,
174
+ zIndex,
175
+ children
176
+ } = props,
177
+ menuContext = useMenuContext(scope),
178
+ rootContext = useMenuRootContext(scope),
179
+ popperContext = PopperPrimitive.usePopperContext(scope),
180
+ menuSubContext = useMenuSubContext(scope),
181
+ themeName = useThemeName(),
182
+ themedChildren = /* @__PURE__ */jsx(Theme, {
183
+ forceClassName: !0,
184
+ name: themeName,
185
+ children
186
+ }),
187
+ content = needsPortalRepropagation() ? /* @__PURE__ */jsx(RepropagateMenuAndMenuRootProvider, {
188
+ menuContext,
189
+ rootContext,
190
+ popperContext,
191
+ menuSubContext,
192
+ scope,
193
+ children: themedChildren
194
+ }) : themedChildren,
195
+ isPresent = forceMount || rootContext.open && menuContext.open;
196
+ return /* @__PURE__ */jsx(Animate, {
197
+ type: "presence",
198
+ present: isPresent,
199
+ children: /* @__PURE__ */jsx(PortalPrimitive, {
200
+ stackZIndex: !0,
201
+ children: /* @__PURE__ */jsx(Fragment, {
202
+ children: /* @__PURE__ */jsx(PortalProvider, {
203
+ scope,
204
+ forceMount,
205
+ children: /* @__PURE__ */jsxs(View, {
206
+ zIndex: zIndex || 100,
207
+ inset: 0,
208
+ position: "absolute",
209
+ children: [!!menuContext.open && !isWeb && /* @__PURE__ */jsx(View, {
210
+ inset: 0,
211
+ position: "absolute",
212
+ onPress: () => menuContext.onOpenChange(!menuContext.open)
213
+ }), content]
214
+ })
215
+ })
216
+ })
217
+ })
218
+ });
219
+ };
220
+ MenuPortal.displayName = PORTAL_NAME;
221
+ const CONTENT_NAME = "MenuContent",
222
+ {
223
+ Provider: MenuContentProvider,
224
+ useStyledContext: useMenuContentContext
225
+ } = createStyledContext(),
226
+ MenuContent = styled(PopperPrimitive.PopperContentFrame, {
227
+ name: CONTENT_NAME
228
+ }).styleable((props, forwardedRef) => {
229
+ const scope = props.scope || MENU_CONTEXT,
230
+ portalContext = usePortalContext(scope),
231
+ {
232
+ forceMount = portalContext.forceMount,
233
+ ...contentProps
234
+ } = props,
235
+ rootContext = useMenuRootContext(scope);
236
+ return /* @__PURE__ */jsx(Collection.Provider, {
237
+ scope,
238
+ children: /* @__PURE__ */jsx(Collection.Slot, {
239
+ scope,
240
+ children: rootContext.modal ? /* @__PURE__ */jsx(MenuRootContentModal, {
241
+ ...contentProps,
242
+ ref: forwardedRef
243
+ }) : /* @__PURE__ */jsx(MenuRootContentNonModal, {
244
+ ...contentProps,
245
+ ref: forwardedRef
246
+ })
247
+ })
248
+ });
249
+ }),
250
+ MenuRootContentModal = React.forwardRef((props, forwardedRef) => {
251
+ const scope = props.scope || MENU_CONTEXT,
252
+ context = useMenuContext(scope),
253
+ ref = React.useRef(null),
254
+ composedRefs = useComposedRefs(forwardedRef, ref);
255
+ return /* @__PURE__ */jsx(MenuContentImpl, {
256
+ ...props,
257
+ scope,
258
+ ref: composedRefs,
259
+ trapFocus: context.open,
260
+ disableOutsidePointerEvents: context.open,
261
+ disableOutsideScroll: !1,
262
+ onFocusOutside: composeEventHandlers(props.onFocusOutside, event => event.preventDefault(), {
263
+ checkDefaultPrevented: !1
264
+ }),
265
+ onDismiss: () => context.onOpenChange(!1)
266
+ });
267
+ }),
268
+ MenuRootContentNonModal = React.forwardRef((props, forwardedRef) => {
269
+ const scope = props.scope || MENU_CONTEXT,
270
+ context = useMenuContext(scope);
271
+ return /* @__PURE__ */jsx(MenuContentImpl, {
272
+ ...props,
273
+ scope,
274
+ ref: forwardedRef,
275
+ trapFocus: !1,
276
+ disableOutsidePointerEvents: !1,
277
+ disableOutsideScroll: !1,
278
+ onDismiss: () => context.onOpenChange(!1)
279
+ });
280
+ }),
281
+ MenuContentImpl = React.forwardRef((props, forwardedRef) => {
282
+ const {
283
+ scope = MENU_CONTEXT,
284
+ loop = !1,
285
+ trapFocus,
286
+ onOpenAutoFocus,
287
+ onCloseAutoFocus,
288
+ disableOutsidePointerEvents,
289
+ onEntryFocus,
290
+ onEscapeKeyDown,
291
+ onPointerDownOutside,
292
+ onFocusOutside,
293
+ onInteractOutside,
294
+ onDismiss,
295
+ disableOutsideScroll,
296
+ disableDismissOnScroll = !1,
297
+ unstyled = process.env.TAMAGUI_HEADLESS === "1",
298
+ ...contentProps
299
+ } = props,
300
+ context = useMenuContext(scope),
301
+ rootContext = useMenuRootContext(scope),
302
+ getItems = useCollection(scope),
303
+ [currentItemId, setCurrentItemId] = React.useState(null),
304
+ contentRef = React.useRef(null),
305
+ focusableContentRef = React.useRef(null),
306
+ composedRefs = useComposedRefs(forwardedRef, contentRef, context.onContentChange),
307
+ timerRef = React.useRef(0),
308
+ searchRef = React.useRef(""),
309
+ pointerGraceTimerRef = React.useRef(0),
310
+ pointerGraceIntentRef = React.useRef(null),
311
+ pointerDirRef = React.useRef("right"),
312
+ lastPointerXRef = React.useRef(0),
313
+ handleTypeaheadSearch = key => {
314
+ const search = searchRef.current + key,
315
+ items = getItems().filter(item => !item.disabled),
316
+ currentItem = document.activeElement,
317
+ currentMatch = items.find(item => item.ref.current === currentItem)?.textValue,
318
+ values = items.map(item => item.textValue),
319
+ nextMatch = getNextMatch(values, search, currentMatch),
320
+ newItem = items.find(item => item.textValue === nextMatch)?.ref.current;
321
+ (function updateSearch(value) {
322
+ searchRef.current = value, clearTimeout(timerRef.current), value !== "" && (timerRef.current = setTimeout(() => updateSearch(""), 1e3));
323
+ })(search), newItem && setTimeout(() => newItem.focus());
324
+ };
325
+ React.useEffect(() => () => clearTimeout(timerRef.current), []), React.useEffect(() => {
326
+ if (!isWeb || !context.open) return;
327
+ const frame = requestAnimationFrame(() => {
328
+ const el = contentRef.current?.querySelector("[data-tamagui-menu-content]");
329
+ el && (focusableContentRef.current = el);
330
+ });
331
+ return () => cancelAnimationFrame(frame);
332
+ }, [context.open]), React.useEffect(() => {
333
+ if (!isWeb || disableDismissOnScroll || !context.open) return;
334
+ const handleScroll = event => {
335
+ const target = event.target;
336
+ contentRef.current?.contains(target) || onDismiss?.();
337
+ };
338
+ return window.addEventListener("scroll", handleScroll, {
339
+ capture: !0,
340
+ passive: !0
341
+ }), () => {
342
+ window.removeEventListener("scroll", handleScroll, {
343
+ capture: !0
344
+ });
345
+ };
346
+ }, [disableDismissOnScroll, context.open, onDismiss]), isWeb && useFocusGuards();
347
+ const isPointerMovingToSubmenu = React.useCallback(event => {
348
+ const isMovingTowards = pointerDirRef.current === pointerGraceIntentRef.current?.side,
349
+ inArea = isPointerInGraceArea(event, pointerGraceIntentRef.current?.area);
350
+ return isMovingTowards && inArea;
351
+ }, []),
352
+ content = /* @__PURE__ */jsx(PopperPrimitive.PopperContent, {
353
+ role: "menu",
354
+ tabIndex: -1,
355
+ unstyled,
356
+ ...(!unstyled && {
357
+ backgroundColor: "$background",
358
+ borderWidth: 1,
359
+ borderColor: "$borderColor",
360
+ outlineWidth: 0,
361
+ minWidth: 180
362
+ }),
363
+ "aria-orientation": "vertical",
364
+ "data-state": getOpenState(context.open),
365
+ "data-tamagui-menu-content": "",
366
+ dir: rootContext.dir,
367
+ scope: scope || MENU_CONTEXT,
368
+ ...contentProps,
369
+ ref: composedRefs,
370
+ className: contentProps.transition ? void 0 : contentProps.className,
371
+ ...(isWeb ? {
372
+ onKeyDown: composeEventHandlers(contentProps.onKeyDown, event => {
373
+ const isKeyDownInside = event.target.closest("[data-tamagui-menu-content]") === event.currentTarget,
374
+ isModifierKey = event.ctrlKey || event.altKey || event.metaKey,
375
+ isCharacterKey = event.key.length === 1;
376
+ isKeyDownInside && (event.key === "Tab" && event.preventDefault(), !isModifierKey && isCharacterKey && handleTypeaheadSearch(event.key));
377
+ const isOnContentFrame = event.target.hasAttribute("data-tamagui-menu-content");
378
+ if (!isKeyDownInside || !isOnContentFrame || !FIRST_LAST_KEYS.includes(event.key)) return;
379
+ event.preventDefault();
380
+ const candidateNodes = getItems().filter(item => !item.disabled).map(item => item.ref.current);
381
+ LAST_KEYS.includes(event.key) && candidateNodes.reverse(), focusFirst(candidateNodes, {
382
+ focusVisible: !0
383
+ });
384
+ }),
385
+ // TODO
386
+ // @ts-ignore
387
+ onBlur: composeEventHandlers(props.onBlur, event => {
388
+ event.currentTarget?.contains(event.target) || (clearTimeout(timerRef.current), searchRef.current = "");
389
+ }),
390
+ // TODO
391
+ onPointerMove: composeEventHandlers(props.onPointerMove, event => {
392
+ if (event.pointerType !== "mouse") return;
393
+ const target = event.target,
394
+ pointerXHasChanged = lastPointerXRef.current !== event.clientX;
395
+ if (event.currentTarget?.contains(target) && pointerXHasChanged) {
396
+ const newDir = event.clientX > lastPointerXRef.current ? "right" : "left";
397
+ pointerDirRef.current = newDir, lastPointerXRef.current = event.clientX;
398
+ }
399
+ })
400
+ } : {})
401
+ });
402
+ return /* @__PURE__ */jsx(MenuContentProvider, {
403
+ scope,
404
+ searchRef,
405
+ onItemEnter: React.useCallback(event => {
406
+ isPointerMovingToSubmenu(event) && event.preventDefault();
407
+ }, [isPointerMovingToSubmenu]),
408
+ onItemLeave: React.useCallback(event => {
409
+ isPointerMovingToSubmenu(event) || (focusableContentRef.current?.focus(), setCurrentItemId(null));
410
+ }, [isPointerMovingToSubmenu]),
411
+ onTriggerLeave: React.useCallback(event => {
412
+ isPointerMovingToSubmenu(event) && event.preventDefault();
413
+ }, [isPointerMovingToSubmenu]),
414
+ pointerGraceTimerRef,
415
+ onPointerGraceIntentChange: React.useCallback(intent => {
416
+ pointerGraceIntentRef.current = intent;
417
+ }, []),
418
+ children: /* @__PURE__ */jsx(RemoveScroll, {
419
+ enabled: disableOutsideScroll,
420
+ children: /* @__PURE__ */jsx(FocusScope, {
421
+ asChild: !1,
422
+ trapped: trapFocus,
423
+ onMountAutoFocus: composeEventHandlers(onOpenAutoFocus, event => {
424
+ event.preventDefault(), document.querySelector("[data-tamagui-menu-content]")?.focus({
425
+ preventScroll: !0
426
+ });
427
+ }),
428
+ onUnmountAutoFocus: onCloseAutoFocus,
429
+ children: /* @__PURE__ */jsx(DismissableLayer, {
430
+ disableOutsidePointerEvents,
431
+ onEscapeKeyDown,
432
+ onPointerDownOutside,
433
+ onFocusOutside,
434
+ onInteractOutside,
435
+ onDismiss,
436
+ asChild: !0,
437
+ children: /* @__PURE__ */jsx(RovingFocusGroup, {
438
+ asChild: !0,
439
+ __scopeRovingFocusGroup: scope || MENU_CONTEXT,
440
+ dir: rootContext.dir,
441
+ orientation: "vertical",
442
+ loop,
443
+ currentTabStopId: currentItemId,
444
+ onCurrentTabStopIdChange: setCurrentItemId,
445
+ onEntryFocus: composeEventHandlers(onEntryFocus, event => {
446
+ rootContext.isUsingKeyboardRef.current || event.preventDefault();
447
+ }),
448
+ children: content
449
+ })
450
+ })
451
+ })
452
+ })
453
+ });
454
+ });
455
+ MenuContent.displayName = CONTENT_NAME;
456
+ const ITEM_NAME = "MenuItem",
457
+ ITEM_SELECT = "menu.itemSelect",
458
+ MenuItem = _Item.styleable((props, forwardedRef) => {
459
+ const {
460
+ disabled = !1,
461
+ onSelect,
462
+ preventCloseOnSelect,
463
+ children,
464
+ scope = MENU_CONTEXT,
465
+ // filter out native-only props that shouldn't reach the DOM
466
+ // @ts-ignore
467
+ destructive,
468
+ // @ts-ignore
469
+ hidden,
470
+ // @ts-ignore
471
+ androidIconName,
472
+ // @ts-ignore
473
+ iosIconName,
474
+ ...itemProps
475
+ } = props,
476
+ ref = React.useRef(null),
477
+ rootContext = useMenuRootContext(scope),
478
+ contentContext = useMenuContentContext(scope),
479
+ composedRefs = useComposedRefs(forwardedRef, ref),
480
+ isPointerDownRef = React.useRef(!1),
481
+ handleSelect = () => {
482
+ const menuItem = ref.current;
483
+ if (!disabled && menuItem) if (isWeb) {
484
+ const menuItemEl = menuItem,
485
+ itemSelectEvent = new CustomEvent(ITEM_SELECT, {
486
+ bubbles: !0,
487
+ cancelable: !0
488
+ });
489
+ menuItemEl.addEventListener(ITEM_SELECT, event => onSelect?.(event), {
490
+ once: !0
491
+ }), dispatchDiscreteCustomEvent(menuItemEl, itemSelectEvent), itemSelectEvent.defaultPrevented || preventCloseOnSelect ? isPointerDownRef.current = !1 : rootContext.onClose();
492
+ } else onSelect?.({
493
+ target: menuItem
494
+ }), isPointerDownRef.current = !1, preventCloseOnSelect || rootContext.onClose();
495
+ },
496
+ content = typeof children == "string" ? /* @__PURE__ */jsx(Text, {
497
+ children
498
+ }) : children;
499
+ return /* @__PURE__ */jsx(MenuItemImpl, {
500
+ outlineStyle: "none",
501
+ ...itemProps,
502
+ scope,
503
+ ref: composedRefs,
504
+ disabled,
505
+ onPress: composeEventHandlers(props.onPress, handleSelect),
506
+ onPointerDown: event => {
507
+ props.onPointerDown?.(event), isPointerDownRef.current = !0;
508
+ },
509
+ onPointerUp: composeEventHandlers(props.onPointerUp, event => {
510
+ isWeb && (isPointerDownRef.current || event.currentTarget?.click());
511
+ }),
512
+ ...(isWeb ? {
513
+ onKeyDown: composeEventHandlers(props.onKeyDown, event => {
514
+ const isTypingAhead = contentContext.searchRef.current !== "";
515
+ disabled || isTypingAhead && event.key === " " || SELECTION_KEYS.includes(event.key) && (event.currentTarget?.click(), event.preventDefault());
516
+ })
517
+ } : {}),
518
+ children: content
519
+ });
520
+ }),
521
+ MenuItemImpl = React.forwardRef((props, forwardedRef) => {
522
+ const {
523
+ scope = MENU_CONTEXT,
524
+ disabled = !1,
525
+ textValue,
526
+ unstyled = process.env.TAMAGUI_HEADLESS === "1",
527
+ ...itemProps
528
+ } = props,
529
+ contentContext = useMenuContentContext(scope),
530
+ ref = React.useRef(null),
531
+ composedRefs = useComposedRefs(forwardedRef, ref),
532
+ [isFocused, setIsFocused] = React.useState(!1),
533
+ [textContent, setTextContent] = React.useState("");
534
+ return isWeb && React.useEffect(() => {
535
+ const menuItem = ref.current;
536
+ menuItem && setTextContent((menuItem.textContent ?? "").trim());
537
+ }, [itemProps.children]), /* @__PURE__ */jsx(Collection.ItemSlot, {
538
+ scope,
539
+ disabled,
540
+ textValue: textValue ?? textContent,
541
+ children: /* @__PURE__ */jsx(RovingFocusGroup.Item, {
542
+ asChild: !0,
543
+ __scopeRovingFocusGroup: scope,
544
+ focusable: !disabled,
545
+ children: /* @__PURE__ */jsx(_Item, {
546
+ unstyled,
547
+ componentName: ITEM_NAME,
548
+ role: "menuitem",
549
+ "data-highlighted": isFocused ? "" : void 0,
550
+ "aria-disabled": disabled || void 0,
551
+ "data-disabled": disabled ? "" : void 0,
552
+ ...itemProps,
553
+ ref: composedRefs,
554
+ onPointerMove: composeEventHandlers(props.onPointerMove, event => {
555
+ event.pointerType === "mouse" && (disabled ? contentContext.onItemLeave(event) : (contentContext.onItemEnter(event), event.defaultPrevented || event.currentTarget.focus({
556
+ preventScroll: !0,
557
+ focusVisible: !1
558
+ })));
559
+ }),
560
+ onPointerLeave: composeEventHandlers(props.onPointerLeave, event => {
561
+ contentContext.onItemLeave(event);
562
+ }),
563
+ onFocus: composeEventHandlers(props.onFocus, () => setIsFocused(!0)),
564
+ onBlur: composeEventHandlers(props.onBlur, () => setIsFocused(!1))
565
+ })
566
+ })
567
+ });
568
+ });
569
+ MenuItem.displayName = ITEM_NAME;
570
+ const ITEM_TITLE_NAME = "MenuItemTitle",
571
+ MenuItemTitle = _Title.styleable((props, forwardedRef) => /* @__PURE__ */jsx(_Title, {
572
+ ...props,
573
+ ref: forwardedRef
574
+ }));
575
+ MenuItemTitle.displayName = ITEM_TITLE_NAME;
576
+ const ITEM_SUB_TITLE_NAME = "MenuItemSubTitle",
577
+ MenuItemSubTitle = _SubTitle.styleable((props, forwardedRef) => /* @__PURE__ */jsx(_SubTitle, {
578
+ ...props,
579
+ ref: forwardedRef
580
+ }));
581
+ MenuItemSubTitle.displayName = ITEM_SUB_TITLE_NAME;
582
+ const ITEM_IMAGE = "MenuItemImage",
583
+ MenuItemImage = React.forwardRef((props, forwardedRef) => {
584
+ const {
585
+ // @ts-ignore - native menu ios config
586
+ ios,
587
+ // @ts-ignore
588
+ androidIconName,
589
+ // @ts-ignore
590
+ iosIconName,
591
+ ...rest
592
+ } = props;
593
+ return /* @__PURE__ */jsx(_Image, {
594
+ ...rest,
595
+ ref: forwardedRef
596
+ });
597
+ });
598
+ MenuItemImage.displayName = ITEM_IMAGE;
599
+ const ITEM_ICON = "MenuItemIcon",
600
+ MenuItemIcon = _Icon.styleable((props, forwardedRef) => {
601
+ const {
602
+ // @ts-ignore
603
+ ios,
604
+ // @ts-ignore
605
+ android,
606
+ // @ts-ignore
607
+ androidIconName,
608
+ // @ts-ignore
609
+ iosIconName,
610
+ ...rest
611
+ } = props;
612
+ return /* @__PURE__ */jsx(_Icon, {
613
+ ...rest,
614
+ ref: forwardedRef
615
+ });
616
+ });
617
+ MenuItemIcon.displayName = ITEM_ICON;
618
+ const CHECKBOX_ITEM_NAME = "MenuCheckboxItem",
619
+ MenuCheckboxItem = _Item.styleable((props, forwardedRef) => {
620
+ const {
621
+ checked = !1,
622
+ onCheckedChange,
623
+ scope = MENU_CONTEXT,
624
+ // filter out native-only props
625
+ // @ts-ignore - native menu value state
626
+ value,
627
+ // @ts-ignore - native menu value change handler
628
+ onValueChange,
629
+ ...checkboxItemProps
630
+ } = props;
631
+ return /* @__PURE__ */jsx(ItemIndicatorProvider, {
632
+ scope,
633
+ checked,
634
+ children: /* @__PURE__ */jsx(MenuItem, {
635
+ componentName: CHECKBOX_ITEM_NAME,
636
+ role: isWeb ? "menuitemcheckbox" : "menuitem",
637
+ "aria-checked": isIndeterminate(checked) ? "mixed" : checked,
638
+ ...checkboxItemProps,
639
+ scope,
640
+ ref: forwardedRef,
641
+ "data-state": getCheckedState(checked),
642
+ onSelect: composeEventHandlers(checkboxItemProps.onSelect, () => onCheckedChange?.(isIndeterminate(checked) ? !0 : !checked), {
643
+ checkDefaultPrevented: !1
644
+ })
645
+ })
646
+ });
647
+ });
648
+ MenuCheckboxItem.displayName = CHECKBOX_ITEM_NAME;
649
+ const RADIO_GROUP_NAME = "MenuRadioGroup",
650
+ {
651
+ Provider: RadioGroupProvider,
652
+ useStyledContext: useRadioGroupContext
653
+ } = createStyledContext(),
654
+ MenuRadioGroup = _MenuGroup.styleable((props, forwardedRef) => {
655
+ const {
656
+ value,
657
+ onValueChange,
658
+ scope = MENU_CONTEXT,
659
+ ...groupProps
660
+ } = props,
661
+ handleValueChange = useCallbackRef(onValueChange);
662
+ return /* @__PURE__ */jsx(RadioGroupProvider, {
663
+ scope,
664
+ value,
665
+ onValueChange: handleValueChange,
666
+ children: /* @__PURE__ */jsx(_MenuGroup, {
667
+ componentName: RADIO_GROUP_NAME,
668
+ ...groupProps,
669
+ ref: forwardedRef
670
+ })
671
+ });
672
+ });
673
+ MenuRadioGroup.displayName = RADIO_GROUP_NAME;
674
+ const RADIO_ITEM_NAME = "MenuRadioItem",
675
+ MenuRadioItem = _Item.styleable((props, forwardedRef) => {
676
+ const {
677
+ value,
678
+ scope = MENU_CONTEXT,
679
+ ...radioItemProps
680
+ } = props,
681
+ context = useRadioGroupContext(scope),
682
+ checked = value === context.value;
683
+ return /* @__PURE__ */jsx(ItemIndicatorProvider, {
684
+ scope,
685
+ checked,
686
+ children: /* @__PURE__ */jsx(MenuItem, {
687
+ componentName: RADIO_ITEM_NAME,
688
+ ...radioItemProps,
689
+ scope,
690
+ "aria-checked": checked,
691
+ ref: forwardedRef,
692
+ role: isWeb ? "menuitemradio" : "menuitem",
693
+ "data-state": getCheckedState(checked),
694
+ onSelect: composeEventHandlers(radioItemProps.onSelect, () => context.onValueChange?.(value), {
695
+ checkDefaultPrevented: !1
696
+ })
697
+ })
698
+ });
699
+ });
700
+ MenuRadioItem.displayName = RADIO_ITEM_NAME;
701
+ const ITEM_INDICATOR_NAME = "MenuItemIndicator",
702
+ {
703
+ Provider: ItemIndicatorProvider,
704
+ useStyledContext: useItemIndicatorContext
705
+ } = createStyledContext(),
706
+ MenuItemIndicator = _Indicator.styleable((props, forwardedRef) => {
707
+ const {
708
+ scope = MENU_CONTEXT,
709
+ forceMount,
710
+ ...itemIndicatorProps
711
+ } = props,
712
+ indicatorContext = useItemIndicatorContext(scope);
713
+ return /* @__PURE__ */jsx(Presence, {
714
+ children: forceMount || isIndeterminate(indicatorContext.checked) || indicatorContext.checked === !0 ? /* @__PURE__ */jsx(_Indicator, {
715
+ componentName: ITEM_INDICATOR_NAME,
716
+ render: "span",
717
+ ...itemIndicatorProps,
718
+ ref: forwardedRef,
719
+ "data-state": getCheckedState(indicatorContext.checked)
720
+ }) : null
721
+ });
722
+ });
723
+ MenuItemIndicator.displayName = ITEM_INDICATOR_NAME;
724
+ const MenuArrow = React.forwardRef(function (props, forwardedRef) {
725
+ const {
726
+ scope = MENU_CONTEXT,
727
+ unstyled = process.env.TAMAGUI_HEADLESS === "1",
728
+ ...rest
729
+ } = props;
730
+ return /* @__PURE__ */jsx(PopperPrimitive.PopperArrow, {
731
+ scope,
732
+ componentName: "PopperArrow",
733
+ unstyled,
734
+ ...(!unstyled && {
735
+ backgroundColor: "$background"
736
+ }),
737
+ ...rest,
738
+ ref: forwardedRef
739
+ });
740
+ }),
741
+ SUB_NAME = "MenuSub",
742
+ {
743
+ Provider: MenuSubProvider,
744
+ useStyledContext: useMenuSubContext
745
+ } = createStyledContext(),
746
+ MenuSub = props => {
747
+ const isTouchDevice = useIsTouchDevice(),
748
+ {
749
+ scope = MENU_CONTEXT
750
+ } = props,
751
+ rootContext = useMenuRootContext(scope),
752
+ parentSide = PopperPrimitive.usePopperContext(scope).placement?.split("-")[0],
753
+ isNestedSubmenu = parentSide === "left" || parentSide === "right",
754
+ defaultPlacement = isTouchDevice ? "bottom" : isNestedSubmenu ? `${parentSide}-start` : rootContext.dir === "rtl" ? "left-start" : "right-start",
755
+ {
756
+ children,
757
+ open = !1,
758
+ onOpenChange,
759
+ allowFlip: allowFlipProp = {
760
+ padding: 10
761
+ },
762
+ stayInFrame = {
763
+ padding: 10
764
+ },
765
+ placement = defaultPlacement,
766
+ ...rest
767
+ } = props,
768
+ allowFlip = React.useMemo(() => {
769
+ if (!isNestedSubmenu || typeof allowFlipProp == "boolean" || allowFlipProp.fallbackPlacements) return allowFlipProp;
770
+ const side = placement.split("-")[0],
771
+ align = placement.split("-")[1] || "start",
772
+ otherAlign = align === "start" ? "end" : "start";
773
+ if (side === "left" || side === "right") {
774
+ const oppositeSide = side === "right" ? "left" : "right";
775
+ return {
776
+ ...(typeof allowFlipProp == "object" ? allowFlipProp : {}),
777
+ fallbackPlacements: [`${side}-${otherAlign}`, `${oppositeSide}-${align}`, `${oppositeSide}-${otherAlign}`]
778
+ };
779
+ }
780
+ return allowFlipProp;
781
+ }, [isNestedSubmenu, allowFlipProp, placement]),
782
+ parentMenuContext = useMenuContext(scope),
783
+ [trigger, setTrigger] = React.useState(null),
784
+ [content, setContent] = React.useState(null),
785
+ handleOpenChange = useCallbackRef(onOpenChange);
786
+ return React.useEffect(() => (parentMenuContext.open === !1 && handleOpenChange(!1), () => handleOpenChange(!1)), [parentMenuContext.open, handleOpenChange]), /* @__PURE__ */jsx(PopperPrimitive.Popper, {
787
+ open,
788
+ placement,
789
+ allowFlip,
790
+ stayInFrame,
791
+ ...rest,
792
+ scope,
793
+ children: /* @__PURE__ */jsx(MenuProvider, {
794
+ scope,
795
+ open,
796
+ onOpenChange: handleOpenChange,
797
+ content,
798
+ onContentChange: setContent,
799
+ children: /* @__PURE__ */jsx(MenuSubProvider, {
800
+ scope,
801
+ contentId: useId(),
802
+ triggerId: useId(),
803
+ trigger,
804
+ onTriggerChange: setTrigger,
805
+ children
806
+ })
807
+ })
808
+ });
809
+ };
810
+ MenuSub.displayName = SUB_NAME;
811
+ const SUB_TRIGGER_NAME = "MenuSubTrigger",
812
+ MenuSubTrigger = React.forwardRef((props, forwardedRef) => {
813
+ const scope = props.scope || MENU_CONTEXT,
814
+ context = useMenuContext(scope),
815
+ rootContext = useMenuRootContext(scope),
816
+ subContext = useMenuSubContext(scope),
817
+ contentContext = useMenuContentContext(scope),
818
+ popperContext = PopperPrimitive.usePopperContext(scope),
819
+ openTimerRef = React.useRef(null),
820
+ {
821
+ pointerGraceTimerRef,
822
+ onPointerGraceIntentChange
823
+ } = contentContext,
824
+ effectiveDir = rootContext.dir,
825
+ clearOpenTimer = React.useCallback(() => {
826
+ openTimerRef.current && window.clearTimeout(openTimerRef.current), openTimerRef.current = null;
827
+ }, []);
828
+ return React.useEffect(() => clearOpenTimer, [clearOpenTimer]), React.useEffect(() => {
829
+ const pointerGraceTimer = pointerGraceTimerRef.current;
830
+ return () => {
831
+ window.clearTimeout(pointerGraceTimer), onPointerGraceIntentChange(null);
832
+ };
833
+ }, [pointerGraceTimerRef, onPointerGraceIntentChange]), /* @__PURE__ */jsx(MenuAnchor, {
834
+ componentName: SUB_TRIGGER_NAME,
835
+ asChild: "except-style",
836
+ scope,
837
+ children: /* @__PURE__ */jsx(MenuItemImpl, {
838
+ id: subContext.triggerId,
839
+ "aria-haspopup": "menu",
840
+ "aria-expanded": context.open,
841
+ "aria-controls": subContext.contentId,
842
+ "data-state": getOpenState(context.open),
843
+ outlineStyle: "none",
844
+ ...props,
845
+ ref: composeRefs(forwardedRef, subContext.onTriggerChange),
846
+ onPress: event => {
847
+ props.onPress?.(event), !(props.disabled || event.defaultPrevented) && (isWeb && event.currentTarget.focus(), context.open || context.onOpenChange(!0));
848
+ },
849
+ onPointerMove: composeEventHandlers(props.onPointerMove,
850
+ // @ts-ignore
851
+ whenMouse(event => {
852
+ contentContext.onItemEnter(event), !event.defaultPrevented && !props.disabled && !context.open && !openTimerRef.current && (contentContext.onPointerGraceIntentChange(null), openTimerRef.current = window.setTimeout(() => {
853
+ context.onOpenChange(!0), clearOpenTimer();
854
+ }, 100));
855
+ })),
856
+ onPointerLeave: composeEventHandlers(props.onPointerLeave, eventIn => {
857
+ const event = eventIn;
858
+ clearOpenTimer();
859
+ const contentRect = context.content?.getBoundingClientRect();
860
+ if (contentRect) {
861
+ const contentEl = context.content,
862
+ side = (contentEl?.dataset?.side ? contentEl : contentEl?.querySelector("[data-side]"))?.dataset?.side || "right",
863
+ rightSide = side === "right",
864
+ bleed = rightSide ? -5 : 5,
865
+ contentNearEdge = contentRect[rightSide ? "left" : "right"],
866
+ contentFarEdge = contentRect[rightSide ? "right" : "left"],
867
+ polygon = {
868
+ area: [
869
+ // Apply a bleed on clientX to ensure that our exit point is
870
+ // consistently within polygon bounds
871
+ {
872
+ x: event.clientX + bleed,
873
+ y: event.clientY
874
+ }, {
875
+ x: contentNearEdge,
876
+ y: contentRect.top
877
+ }, {
878
+ x: contentFarEdge,
879
+ y: contentRect.top
880
+ }, {
881
+ x: contentFarEdge,
882
+ y: contentRect.bottom
883
+ }, {
884
+ x: contentNearEdge,
885
+ y: contentRect.bottom
886
+ }],
887
+ side
888
+ };
889
+ contentContext.onPointerGraceIntentChange(polygon), window.clearTimeout(pointerGraceTimerRef.current), pointerGraceTimerRef.current = window.setTimeout(() => contentContext.onPointerGraceIntentChange(null), 300);
890
+ } else if (isWeb && subContext.trigger) {
891
+ const triggerRect = subContext.trigger?.getBoundingClientRect();
892
+ if (triggerRect) {
893
+ const placementSide = popperContext.placement?.split("-")[0],
894
+ side = placementSide === "left" || placementSide === "right" ? placementSide : rootContext.dir === "rtl" ? "left" : "right",
895
+ rightSide = side === "right",
896
+ bleed = rightSide ? -5 : 5,
897
+ nearEdge = rightSide ? triggerRect.right + 4 : triggerRect.left - 4,
898
+ farEdge = rightSide ? nearEdge + 200 : nearEdge - 200,
899
+ polygon = {
900
+ area: [{
901
+ x: event.clientX + bleed,
902
+ y: event.clientY
903
+ }, {
904
+ x: nearEdge,
905
+ y: triggerRect.top - 50
906
+ }, {
907
+ x: farEdge,
908
+ y: triggerRect.top - 50
909
+ }, {
910
+ x: farEdge,
911
+ y: triggerRect.bottom + 50
912
+ }, {
913
+ x: nearEdge,
914
+ y: triggerRect.bottom + 50
915
+ }],
916
+ side
917
+ };
918
+ contentContext.onPointerGraceIntentChange(polygon), window.clearTimeout(pointerGraceTimerRef.current), pointerGraceTimerRef.current = window.setTimeout(() => contentContext.onPointerGraceIntentChange(null), 300);
919
+ }
920
+ } else {
921
+ if (contentContext.onTriggerLeave(event), event.defaultPrevented) return;
922
+ contentContext.onPointerGraceIntentChange(null);
923
+ }
924
+ }),
925
+ ...(isWeb ? {
926
+ onKeyDown: composeEventHandlers(props.onKeyDown, event => {
927
+ const isTypingAhead = contentContext.searchRef.current !== "";
928
+ if (props.disabled || isTypingAhead && event.key === " ") return;
929
+ if (SUB_OPEN_KEYS[effectiveDir].includes(event.key)) {
930
+ if (context.open && context.content) {
931
+ const firstItem = context.content.querySelector?.('[role="menuitem"]:not([data-disabled])');
932
+ if (firstItem) {
933
+ firstItem.focus({
934
+ focusVisible: !0
935
+ }), event.preventDefault();
936
+ return;
937
+ }
938
+ }
939
+ const triggerEl = event.currentTarget;
940
+ popperContext.refs?.setReference(triggerEl), context.onOpenChange(!0), requestAnimationFrame(() => {
941
+ popperContext.update?.();
942
+ }), context.content?.focus({
943
+ focusVisible: !0
944
+ }), event.preventDefault();
945
+ }
946
+ })
947
+ } : null)
948
+ })
949
+ });
950
+ });
951
+ MenuSubTrigger.displayName = SUB_TRIGGER_NAME;
952
+ const SUB_CONTENT_NAME = "MenuSubContent",
953
+ MenuSubContent = styled(PopperPrimitive.PopperContentFrame, {
954
+ name: SUB_CONTENT_NAME
955
+ }).styleable((props, forwardedRef) => {
956
+ const scope = props.scope || MENU_CONTEXT,
957
+ portalContext = usePortalContext(scope),
958
+ {
959
+ forceMount = portalContext.forceMount,
960
+ ...subContentProps
961
+ } = props,
962
+ context = useMenuContext(scope),
963
+ rootContext = useMenuRootContext(scope),
964
+ subContext = useMenuSubContext(scope),
965
+ popperContext = PopperPrimitive.usePopperContext(scope),
966
+ ref = React.useRef(null),
967
+ composedRefs = useComposedRefs(forwardedRef, ref),
968
+ placementSide = popperContext.placement?.split("-")[0],
969
+ dataSide = placementSide === "left" || placementSide === "right" ? placementSide : rootContext.dir === "rtl" ? "left" : "right",
970
+ effectiveDir = rootContext.dir;
971
+ return /* @__PURE__ */jsx(Collection.Provider, {
972
+ scope,
973
+ children: /* @__PURE__ */jsx(Collection.Slot, {
974
+ scope,
975
+ children: /* @__PURE__ */jsx(MenuContentImpl, {
976
+ id: subContext.contentId,
977
+ "aria-labelledby": subContext.triggerId,
978
+ ...subContentProps,
979
+ ref: composedRefs,
980
+ "data-side": dataSide,
981
+ disableOutsidePointerEvents: !1,
982
+ disableOutsideScroll: !1,
983
+ trapFocus: !1,
984
+ onOpenAutoFocus: event => {
985
+ if (rootContext.isUsingKeyboardRef.current) {
986
+ const root = ref.current;
987
+ (root?.querySelector?.("[data-tamagui-menu-content]") || root)?.focus({
988
+ preventScroll: !0
989
+ });
990
+ }
991
+ event.preventDefault();
992
+ },
993
+ onCloseAutoFocus: event => event.preventDefault(),
994
+ onFocusOutside: composeEventHandlers(props.onFocusOutside, event => {
995
+ event.target !== subContext.trigger && context.onOpenChange(!1);
996
+ }),
997
+ onEscapeKeyDown: composeEventHandlers(props.onEscapeKeyDown, event => {
998
+ context.onOpenChange(!1), subContext.trigger?.focus({
999
+ focusVisible: !0
1000
+ }), event.preventDefault();
1001
+ }),
1002
+ ...(isWeb ? {
1003
+ onKeyDown: composeEventHandlers(props.onKeyDown, event => {
1004
+ const isKeyDownInside = event.currentTarget.contains(event.target),
1005
+ isCloseKey = SUB_CLOSE_KEYS[effectiveDir].includes(event.key);
1006
+ isKeyDownInside && isCloseKey && (context.onOpenChange(!1), subContext.trigger?.focus({
1007
+ focusVisible: !0
1008
+ }), event.preventDefault());
1009
+ })
1010
+ } : null)
1011
+ })
1012
+ })
1013
+ });
1014
+ });
1015
+ MenuSubContent.displayName = SUB_CONTENT_NAME;
1016
+ const Anchor = MenuAnchor,
1017
+ Portal = MenuPortal,
1018
+ Content = MenuContent,
1019
+ Group = _MenuGroup.styleable((props, ref) => /* @__PURE__ */jsx(_MenuGroup, {
1020
+ ...props,
1021
+ ref
1022
+ }));
1023
+ Group.displayName = "MenuGroup";
1024
+ const Label = _Label.styleable((props, ref) => /* @__PURE__ */jsx(_Label, {
1025
+ ...props,
1026
+ ref
1027
+ }));
1028
+ Label.displayName = "MenuLabel";
1029
+ const Item = MenuItem,
1030
+ CheckboxItem = MenuCheckboxItem,
1031
+ RadioGroup = MenuRadioGroup,
1032
+ RadioItem = MenuRadioItem,
1033
+ ItemIndicator = MenuItemIndicator,
1034
+ Separator = _Separator.styleable((props, ref) => /* @__PURE__ */jsx(_Separator, {
1035
+ ...props,
1036
+ ref
1037
+ }));
1038
+ return Separator.displayName = "MenuSeparator", {
1039
+ Menu: withStaticProperties(MenuComp, {
1040
+ Anchor,
1041
+ Portal,
1042
+ Content,
1043
+ Group,
1044
+ Label,
1045
+ Item,
1046
+ CheckboxItem,
1047
+ RadioGroup,
1048
+ RadioItem,
1049
+ ItemIndicator,
1050
+ Separator,
1051
+ Arrow: MenuArrow,
1052
+ Sub: MenuSub,
1053
+ SubTrigger: MenuSubTrigger,
1054
+ SubContent: MenuSubContent,
1055
+ ItemTitle: MenuItemTitle,
1056
+ ItemSubtitle: MenuItemSubTitle,
1057
+ ItemImage: MenuItemImage,
1058
+ ItemIcon: MenuItemIcon
1059
+ })
1060
+ };
1061
+ }
1062
+ function getOpenState(open) {
1063
+ return open ? "open" : "closed";
1064
+ }
1065
+ function isIndeterminate(checked) {
1066
+ return checked === "indeterminate";
1067
+ }
1068
+ function getCheckedState(checked) {
1069
+ return isIndeterminate(checked) ? "indeterminate" : checked ? "checked" : "unchecked";
1070
+ }
1071
+ function focusFirst(candidates, options) {
1072
+ const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
1073
+ for (const candidate of candidates) if (candidate === PREVIOUSLY_FOCUSED_ELEMENT || (candidate.focus({
1074
+ preventScroll: !0,
1075
+ focusVisible: options?.focusVisible
1076
+ }), document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT)) return;
1077
+ }
1078
+ function wrapArray(array, startIndex) {
1079
+ return array.map((_, index) => array[(startIndex + index) % array.length]);
1080
+ }
1081
+ function getNextMatch(values, search, currentMatch) {
1082
+ const normalizedSearch = search.length > 1 && Array.from(search).every(char => char === search[0]) ? search[0] : search,
1083
+ currentMatchIndex = currentMatch ? values.indexOf(currentMatch) : -1;
1084
+ let wrappedValues = wrapArray(values, Math.max(currentMatchIndex, 0));
1085
+ normalizedSearch.length === 1 && (wrappedValues = wrappedValues.filter(v => v !== currentMatch));
1086
+ const nextMatch = wrappedValues.find(value => value.toLowerCase().startsWith(normalizedSearch.toLowerCase()));
1087
+ return nextMatch !== currentMatch ? nextMatch : void 0;
1088
+ }
1089
+ function isPointInPolygon(point, polygon) {
1090
+ const {
1091
+ x,
1092
+ y
1093
+ } = point;
1094
+ let inside = !1;
1095
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
1096
+ const xi = polygon[i].x,
1097
+ yi = polygon[i].y,
1098
+ xj = polygon[j].x,
1099
+ yj = polygon[j].y;
1100
+ yi > y != yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi && (inside = !inside);
1101
+ }
1102
+ return inside;
1103
+ }
1104
+ function isPointerInGraceArea(event, area) {
1105
+ if (!area) return !1;
1106
+ const cursorPos = {
1107
+ x: event.clientX,
1108
+ y: event.clientY
1109
+ };
1110
+ return isPointInPolygon(cursorPos, area);
1111
+ }
1112
+ export { createBaseMenu };
1113
+ //# sourceMappingURL=createBaseMenu.mjs.map