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