@tamagui/create-menu 2.0.0-rc.3 → 2.0.0-rc.30

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 (129) hide show
  1. package/dist/cjs/MenuPredefined.cjs +1 -1
  2. package/dist/cjs/MenuPredefined.native.js +1 -1
  3. package/dist/cjs/createBaseMenu.cjs +90 -29
  4. package/dist/cjs/createBaseMenu.native.js +109 -34
  5. package/dist/cjs/createBaseMenu.native.js.map +1 -1
  6. package/dist/cjs/createNativeMenu/createNativeMenu.cjs +159 -128
  7. package/dist/cjs/createNativeMenu/createNativeMenu.native.js +258 -238
  8. package/dist/cjs/createNativeMenu/createNativeMenu.native.js.map +1 -1
  9. package/dist/cjs/createNativeMenu/withNativeMenu.cjs +2 -2
  10. package/dist/cjs/createNativeMenu/withNativeMenu.native.js +2 -2
  11. package/dist/cjs/createNativeMenu/withNativeMenu.native.js.map +1 -1
  12. package/dist/cjs/index.cjs +2 -1
  13. package/dist/cjs/index.js +20 -13
  14. package/dist/cjs/index.js.map +1 -1
  15. package/dist/cjs/index.native.js +2 -1
  16. package/dist/cjs/index.native.js.map +1 -1
  17. package/dist/esm/MenuPredefined.mjs +1 -1
  18. package/dist/esm/MenuPredefined.native.js +1 -1
  19. package/dist/esm/createBaseMenu.mjs +91 -30
  20. package/dist/esm/createBaseMenu.mjs.map +1 -1
  21. package/dist/esm/createBaseMenu.native.js +110 -35
  22. package/dist/esm/createBaseMenu.native.js.map +1 -1
  23. package/dist/esm/createNativeMenu/createNativeMenu.mjs +160 -129
  24. package/dist/esm/createNativeMenu/createNativeMenu.mjs.map +1 -1
  25. package/dist/esm/createNativeMenu/createNativeMenu.native.js +228 -208
  26. package/dist/esm/createNativeMenu/createNativeMenu.native.js.map +1 -1
  27. package/dist/esm/createNativeMenu/withNativeMenu.mjs +2 -2
  28. package/dist/esm/createNativeMenu/withNativeMenu.mjs.map +1 -1
  29. package/dist/esm/createNativeMenu/withNativeMenu.native.js +2 -2
  30. package/dist/esm/createNativeMenu/withNativeMenu.native.js.map +1 -1
  31. package/dist/esm/index.js +5 -6
  32. package/dist/esm/index.js.map +1 -6
  33. package/dist/esm/index.mjs +2 -1
  34. package/dist/esm/index.mjs.map +1 -1
  35. package/dist/esm/index.native.js +2 -1
  36. package/dist/esm/index.native.js.map +1 -1
  37. package/dist/jsx/MenuPredefined.mjs +1 -1
  38. package/dist/jsx/MenuPredefined.native.js +1 -1
  39. package/dist/jsx/createBaseMenu.mjs +91 -30
  40. package/dist/jsx/createBaseMenu.mjs.map +1 -1
  41. package/dist/jsx/createBaseMenu.native.js +109 -34
  42. package/dist/jsx/createBaseMenu.native.js.map +1 -1
  43. package/dist/jsx/createNativeMenu/createNativeMenu.mjs +160 -129
  44. package/dist/jsx/createNativeMenu/createNativeMenu.mjs.map +1 -1
  45. package/dist/jsx/createNativeMenu/createNativeMenu.native.js +258 -238
  46. package/dist/jsx/createNativeMenu/createNativeMenu.native.js.map +1 -1
  47. package/dist/jsx/createNativeMenu/withNativeMenu.mjs +2 -2
  48. package/dist/jsx/createNativeMenu/withNativeMenu.mjs.map +1 -1
  49. package/dist/jsx/createNativeMenu/withNativeMenu.native.js +2 -2
  50. package/dist/jsx/createNativeMenu/withNativeMenu.native.js.map +1 -1
  51. package/dist/jsx/index.js +5 -6
  52. package/dist/jsx/index.js.map +1 -6
  53. package/dist/jsx/index.mjs +2 -1
  54. package/dist/jsx/index.mjs.map +1 -1
  55. package/dist/jsx/index.native.js +2 -1
  56. package/dist/jsx/index.native.js.map +1 -1
  57. package/package.json +25 -27
  58. package/src/MenuPredefined.tsx +1 -1
  59. package/src/createBaseMenu.tsx +359 -271
  60. package/src/createNativeMenu/createNativeMenu.tsx +278 -219
  61. package/src/createNativeMenu/createNativeMenuTypes.ts +20 -20
  62. package/src/createNativeMenu/withNativeMenu.tsx +11 -4
  63. package/src/index.tsx +3 -5
  64. package/types/createBaseMenu.d.ts +121 -35
  65. package/types/createBaseMenu.d.ts.map +1 -1
  66. package/types/createNativeMenu/createNativeMenu.d.ts +21 -21
  67. package/types/createNativeMenu/createNativeMenu.d.ts.map +1 -1
  68. package/types/createNativeMenu/createNativeMenuTypes.d.ts +20 -20
  69. package/types/createNativeMenu/createNativeMenuTypes.d.ts.map +1 -1
  70. package/types/createNativeMenu/withNativeMenu.d.ts +3 -3
  71. package/types/createNativeMenu/withNativeMenu.d.ts.map +1 -1
  72. package/types/index.d.ts +3 -2
  73. package/types/index.d.ts.map +1 -1
  74. package/dist/cjs/MenuPredefined.js +0 -168
  75. package/dist/cjs/MenuPredefined.js.map +0 -6
  76. package/dist/cjs/createBaseMenu.js +0 -832
  77. package/dist/cjs/createBaseMenu.js.map +0 -6
  78. package/dist/cjs/createNativeMenu/createNativeMenu.js +0 -177
  79. package/dist/cjs/createNativeMenu/createNativeMenu.js.map +0 -6
  80. package/dist/cjs/createNativeMenu/createNativeMenuTypes.js +0 -14
  81. package/dist/cjs/createNativeMenu/createNativeMenuTypes.js.map +0 -6
  82. package/dist/cjs/createNativeMenu/index.cjs +0 -19
  83. package/dist/cjs/createNativeMenu/index.js +0 -16
  84. package/dist/cjs/createNativeMenu/index.js.map +0 -6
  85. package/dist/cjs/createNativeMenu/index.native.js +0 -22
  86. package/dist/cjs/createNativeMenu/index.native.js.map +0 -1
  87. package/dist/cjs/createNativeMenu/utils.js +0 -66
  88. package/dist/cjs/createNativeMenu/utils.js.map +0 -6
  89. package/dist/cjs/createNativeMenu/withNativeMenu.js +0 -30
  90. package/dist/cjs/createNativeMenu/withNativeMenu.js.map +0 -6
  91. package/dist/esm/MenuPredefined.js +0 -154
  92. package/dist/esm/MenuPredefined.js.map +0 -6
  93. package/dist/esm/createBaseMenu.js +0 -838
  94. package/dist/esm/createBaseMenu.js.map +0 -6
  95. package/dist/esm/createNativeMenu/createNativeMenu.js +0 -156
  96. package/dist/esm/createNativeMenu/createNativeMenu.js.map +0 -6
  97. package/dist/esm/createNativeMenu/createNativeMenuTypes.js +0 -1
  98. package/dist/esm/createNativeMenu/createNativeMenuTypes.js.map +0 -6
  99. package/dist/esm/createNativeMenu/index.js +0 -3
  100. package/dist/esm/createNativeMenu/index.js.map +0 -6
  101. package/dist/esm/createNativeMenu/index.mjs +0 -3
  102. package/dist/esm/createNativeMenu/index.mjs.map +0 -1
  103. package/dist/esm/createNativeMenu/index.native.js +0 -3
  104. package/dist/esm/createNativeMenu/index.native.js.map +0 -1
  105. package/dist/esm/createNativeMenu/utils.js +0 -47
  106. package/dist/esm/createNativeMenu/utils.js.map +0 -6
  107. package/dist/esm/createNativeMenu/withNativeMenu.js +0 -15
  108. package/dist/esm/createNativeMenu/withNativeMenu.js.map +0 -6
  109. package/dist/jsx/MenuPredefined.js +0 -154
  110. package/dist/jsx/MenuPredefined.js.map +0 -6
  111. package/dist/jsx/createBaseMenu.js +0 -838
  112. package/dist/jsx/createBaseMenu.js.map +0 -6
  113. package/dist/jsx/createNativeMenu/createNativeMenu.js +0 -156
  114. package/dist/jsx/createNativeMenu/createNativeMenu.js.map +0 -6
  115. package/dist/jsx/createNativeMenu/createNativeMenuTypes.js +0 -1
  116. package/dist/jsx/createNativeMenu/createNativeMenuTypes.js.map +0 -6
  117. package/dist/jsx/createNativeMenu/index.js +0 -3
  118. package/dist/jsx/createNativeMenu/index.js.map +0 -6
  119. package/dist/jsx/createNativeMenu/index.mjs +0 -3
  120. package/dist/jsx/createNativeMenu/index.mjs.map +0 -1
  121. package/dist/jsx/createNativeMenu/index.native.js +0 -22
  122. package/dist/jsx/createNativeMenu/index.native.js.map +0 -1
  123. package/dist/jsx/createNativeMenu/utils.js +0 -47
  124. package/dist/jsx/createNativeMenu/utils.js.map +0 -6
  125. package/dist/jsx/createNativeMenu/withNativeMenu.js +0 -15
  126. package/dist/jsx/createNativeMenu/withNativeMenu.js.map +0 -6
  127. package/src/createNativeMenu/index.tsx +0 -7
  128. package/types/createNativeMenu/index.d.ts +0 -4
  129. package/types/createNativeMenu/index.d.ts.map +0 -1
@@ -134,7 +134,7 @@ const GROUP_NAME = "MenuGroup",
134
134
  // use focusStyle for highlight since hover triggers focus via onPointerMove
135
135
  // this ensures a single unified highlight for both mouse and keyboard
136
136
  focusStyle: {
137
- backgroundColor: "$backgroundFocus"
137
+ backgroundColor: "$backgroundHover"
138
138
  },
139
139
  pressStyle: {
140
140
  backgroundColor: "$backgroundPress"
@@ -136,7 +136,7 @@ var import_image = require("@tamagui/image"),
136
136
  // use focusStyle for highlight since hover triggers focus via onPointerMove
137
137
  // this ensures a single unified highlight for both mouse and keyboard
138
138
  focusStyle: {
139
- backgroundColor: "$backgroundFocus"
139
+ backgroundColor: "$backgroundHover"
140
140
  },
141
141
  pressStyle: {
142
142
  backgroundColor: "$backgroundPress"
@@ -89,19 +89,29 @@ function createBaseMenu({
89
89
  Label: _Label = import_MenuPredefined.MenuPredefined.MenuLabel
90
90
  }) {
91
91
  const MenuComp = props => {
92
- const {
92
+ const direction = (0, import_use_direction.useDirection)(props.dir),
93
+ defaultPlacement = direction === "rtl" ? "bottom-end" : "bottom-start",
94
+ {
93
95
  scope = MENU_CONTEXT,
94
96
  open = !1,
95
97
  children,
96
98
  dir,
97
99
  onOpenChange,
98
100
  modal = !0,
101
+ allowFlip = {
102
+ padding: 10
103
+ },
104
+ stayInFrame = {
105
+ padding: 10
106
+ },
107
+ placement = defaultPlacement,
108
+ resize = !0,
109
+ offset = 10,
99
110
  ...rest
100
111
  } = props,
101
112
  [content, setContent] = React.useState(null),
102
113
  isUsingKeyboardRef = React.useRef(!1),
103
- handleOpenChange = (0, import_use_callback_ref.useCallbackRef)(onOpenChange),
104
- direction = (0, import_use_direction.useDirection)(dir);
114
+ handleOpenChange = (0, import_use_callback_ref.useCallbackRef)(onOpenChange);
105
115
  return import_web.isWeb && React.useEffect(() => {
106
116
  const handleKeyDown = () => {
107
117
  isUsingKeyboardRef.current = !0, document.addEventListener("pointerdown", handlePointer, {
@@ -126,6 +136,12 @@ function createBaseMenu({
126
136
  };
127
137
  }, []), /* @__PURE__ */(0, import_jsx_runtime.jsx)(PopperPrimitive.Popper, {
128
138
  scope,
139
+ open,
140
+ placement,
141
+ allowFlip,
142
+ stayInFrame,
143
+ resize,
144
+ offset,
129
145
  ...rest,
130
146
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(MenuProvider, {
131
147
  scope,
@@ -217,6 +233,7 @@ function createBaseMenu({
217
233
  type: "presence",
218
234
  present: isPresent,
219
235
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_portal.Portal, {
236
+ stackZIndex: !0,
220
237
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, {
221
238
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(PortalProvider, {
222
239
  scope,
@@ -242,7 +259,9 @@ function createBaseMenu({
242
259
  Provider: MenuContentProvider,
243
260
  useStyledContext: useMenuContentContext
244
261
  } = (0, import_web.createStyledContext)(),
245
- MenuContent = React.forwardRef((props, forwardedRef) => {
262
+ MenuContent = (0, import_web.styled)(PopperPrimitive.PopperContentFrame, {
263
+ name: CONTENT_NAME
264
+ }).styleable((props, forwardedRef) => {
246
265
  const scope = props.scope || MENU_CONTEXT,
247
266
  portalContext = usePortalContext(scope),
248
267
  {
@@ -319,6 +338,7 @@ function createBaseMenu({
319
338
  getItems = useCollection(scope),
320
339
  [currentItemId, setCurrentItemId] = React.useState(null),
321
340
  contentRef = React.useRef(null),
341
+ focusableContentRef = React.useRef(null),
322
342
  composedRefs = (0, import_web.useComposedRefs)(forwardedRef, contentRef, context.onContentChange),
323
343
  timerRef = React.useRef(0),
324
344
  searchRef = React.useRef(""),
@@ -339,9 +359,17 @@ function createBaseMenu({
339
359
  })(search), newItem && setTimeout(() => newItem.focus());
340
360
  };
341
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(() => {
342
369
  if (!import_web.isWeb || disableDismissOnScroll || !context.open) return;
343
- const handleScroll = () => {
344
- onDismiss?.();
370
+ const handleScroll = event => {
371
+ const target = event.target;
372
+ contentRef.current?.contains(target) || onDismiss?.();
345
373
  };
346
374
  return window.addEventListener("scroll", handleScroll, {
347
375
  capture: !0,
@@ -359,8 +387,9 @@ function createBaseMenu({
359
387
  }, []),
360
388
  content = /* @__PURE__ */(0, import_jsx_runtime.jsx)(PopperPrimitive.PopperContent, {
361
389
  role: "menu",
390
+ tabIndex: -1,
391
+ unstyled,
362
392
  ...(!unstyled && {
363
- padding: 4,
364
393
  backgroundColor: "$background",
365
394
  borderWidth: 1,
366
395
  borderColor: "$borderColor",
@@ -413,7 +442,7 @@ function createBaseMenu({
413
442
  isPointerMovingToSubmenu(event) && event.preventDefault();
414
443
  }, [isPointerMovingToSubmenu]),
415
444
  onItemLeave: React.useCallback(event => {
416
- isPointerMovingToSubmenu(event) || (contentRef.current?.focus(), setCurrentItemId(null));
445
+ isPointerMovingToSubmenu(event) || (focusableContentRef.current?.focus(), setCurrentItemId(null));
417
446
  }, [isPointerMovingToSubmenu]),
418
447
  onTriggerLeave: React.useCallback(event => {
419
448
  isPointerMovingToSubmenu(event) && event.preventDefault();
@@ -428,7 +457,9 @@ function createBaseMenu({
428
457
  asChild: !1,
429
458
  trapped: trapFocus,
430
459
  onMountAutoFocus: (0, import_web.composeEventHandlers)(onOpenAutoFocus, event => {
431
- event.preventDefault(), document.querySelector("[data-tamagui-menu-content]")?.focus();
460
+ event.preventDefault(), document.querySelector("[data-tamagui-menu-content]")?.focus({
461
+ preventScroll: !0
462
+ });
432
463
  }),
433
464
  onUnmountAutoFocus: onCloseAutoFocus,
434
465
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_dismissable.Dismissable, {
@@ -460,10 +491,11 @@ function createBaseMenu({
460
491
  MenuContent.displayName = CONTENT_NAME;
461
492
  const ITEM_NAME = "MenuItem",
462
493
  ITEM_SELECT = "menu.itemSelect",
463
- MenuItem = React.forwardRef((props, forwardedRef) => {
494
+ MenuItem = _Item.styleable((props, forwardedRef) => {
464
495
  const {
465
496
  disabled = !1,
466
497
  onSelect,
498
+ preventCloseOnSelect,
467
499
  children,
468
500
  scope = MENU_CONTEXT,
469
501
  // filter out native-only props that shouldn't reach the DOM
@@ -492,10 +524,10 @@ function createBaseMenu({
492
524
  });
493
525
  menuItemEl.addEventListener(ITEM_SELECT, event => onSelect?.(event), {
494
526
  once: !0
495
- }), (0, import_dismissable.dispatchDiscreteCustomEvent)(menuItemEl, itemSelectEvent), itemSelectEvent.defaultPrevented ? isPointerDownRef.current = !1 : rootContext.onClose();
527
+ }), (0, import_dismissable.dispatchDiscreteCustomEvent)(menuItemEl, itemSelectEvent), itemSelectEvent.defaultPrevented || preventCloseOnSelect ? isPointerDownRef.current = !1 : rootContext.onClose();
496
528
  } else onSelect?.({
497
529
  target: menuItem
498
- }), isPointerDownRef.current = !1, rootContext.onClose();
530
+ }), isPointerDownRef.current = !1, preventCloseOnSelect || rootContext.onClose();
499
531
  },
500
532
  content = typeof children == "string" ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_web.Text, {
501
533
  children
@@ -546,12 +578,8 @@ function createBaseMenu({
546
578
  asChild: !0,
547
579
  __scopeRovingFocusGroup: scope,
548
580
  focusable: !disabled,
549
- ...(!unstyled && {
550
- flexDirection: "row",
551
- alignItems: "center"
552
- }),
553
- ...itemProps,
554
581
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(_Item, {
582
+ unstyled,
555
583
  componentName: ITEM_NAME,
556
584
  role: "menuitem",
557
585
  "data-highlighted": isFocused ? "" : void 0,
@@ -624,7 +652,7 @@ function createBaseMenu({
624
652
  });
625
653
  MenuItemIcon.displayName = ITEM_ICON;
626
654
  const CHECKBOX_ITEM_NAME = "MenuCheckboxItem",
627
- MenuCheckboxItem = React.forwardRef((props, forwardedRef) => {
655
+ MenuCheckboxItem = _Item.styleable((props, forwardedRef) => {
628
656
  const {
629
657
  checked = !1,
630
658
  onCheckedChange,
@@ -680,7 +708,7 @@ function createBaseMenu({
680
708
  });
681
709
  MenuRadioGroup.displayName = RADIO_GROUP_NAME;
682
710
  const RADIO_ITEM_NAME = "MenuRadioItem",
683
- MenuRadioItem = React.forwardRef((props, forwardedRef) => {
711
+ MenuRadioItem = _Item.styleable((props, forwardedRef) => {
684
712
  const {
685
713
  value,
686
714
  scope = MENU_CONTEXT,
@@ -738,6 +766,7 @@ function createBaseMenu({
738
766
  return /* @__PURE__ */(0, import_jsx_runtime.jsx)(PopperPrimitive.PopperArrow, {
739
767
  scope,
740
768
  componentName: "PopperArrow",
769
+ unstyled,
741
770
  ...(!unstyled && {
742
771
  backgroundColor: "$background"
743
772
  }),
@@ -751,24 +780,48 @@ function createBaseMenu({
751
780
  useStyledContext: useMenuSubContext
752
781
  } = (0, import_web.createStyledContext)(),
753
782
  MenuSub = props => {
754
- const {
755
- scope = MENU_CONTEXT,
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
+ {
756
792
  children,
757
793
  open = !1,
758
794
  onOpenChange,
759
- allowFlip = {
795
+ allowFlip: allowFlipProp = {
760
796
  padding: 10
761
797
  },
762
798
  stayInFrame = {
763
799
  padding: 10
764
800
  },
801
+ placement = defaultPlacement,
765
802
  ...rest
766
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]),
767
818
  parentMenuContext = useMenuContext(scope),
768
819
  [trigger, setTrigger] = React.useState(null),
769
820
  [content, setContent] = React.useState(null),
770
821
  handleOpenChange = (0, import_use_callback_ref.useCallbackRef)(onOpenChange);
771
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,
772
825
  allowFlip,
773
826
  stayInFrame,
774
827
  ...rest,
@@ -804,8 +857,7 @@ function createBaseMenu({
804
857
  pointerGraceTimerRef,
805
858
  onPointerGraceIntentChange
806
859
  } = contentContext,
807
- placementSide = popperContext.placement?.split("-")[0],
808
- effectiveDir = placementSide === "left" ? "rtl" : placementSide === "right" ? "ltr" : rootContext.dir,
860
+ effectiveDir = rootContext.dir,
809
861
  clearOpenTimer = React.useCallback(() => {
810
862
  openTimerRef.current && window.clearTimeout(openTimerRef.current), openTimerRef.current = null;
811
863
  }, []);
@@ -874,8 +926,8 @@ function createBaseMenu({
874
926
  } else if (import_web.isWeb && subContext.trigger) {
875
927
  const triggerRect = subContext.trigger?.getBoundingClientRect();
876
928
  if (triggerRect) {
877
- const placementSide2 = popperContext.placement?.split("-")[0],
878
- side = placementSide2 === "left" || placementSide2 === "right" ? placementSide2 : rootContext.dir === "rtl" ? "left" : "right",
929
+ const placementSide = popperContext.placement?.split("-")[0],
930
+ side = placementSide === "left" || placementSide === "right" ? placementSide : rootContext.dir === "rtl" ? "left" : "right",
879
931
  rightSide = side === "right",
880
932
  bleed = rightSide ? -5 : 5,
881
933
  nearEdge = rightSide ? triggerRect.right + 4 : triggerRect.left - 4,
@@ -934,7 +986,9 @@ function createBaseMenu({
934
986
  });
935
987
  MenuSubTrigger.displayName = SUB_TRIGGER_NAME;
936
988
  const SUB_CONTENT_NAME = "MenuSubContent",
937
- MenuSubContent = React.forwardRef((props, forwardedRef) => {
989
+ MenuSubContent = (0, import_web.styled)(PopperPrimitive.PopperContentFrame, {
990
+ name: SUB_CONTENT_NAME
991
+ }).styleable((props, forwardedRef) => {
938
992
  const scope = props.scope || MENU_CONTEXT,
939
993
  portalContext = usePortalContext(scope),
940
994
  {
@@ -949,7 +1003,7 @@ function createBaseMenu({
949
1003
  composedRefs = (0, import_web.useComposedRefs)(forwardedRef, ref),
950
1004
  placementSide = popperContext.placement?.split("-")[0],
951
1005
  dataSide = placementSide === "left" || placementSide === "right" ? placementSide : rootContext.dir === "rtl" ? "left" : "right",
952
- effectiveDir = placementSide === "left" ? "rtl" : placementSide === "right" ? "ltr" : rootContext.dir;
1006
+ effectiveDir = rootContext.dir;
953
1007
  return /* @__PURE__ */(0, import_jsx_runtime.jsx)(Collection.Provider, {
954
1008
  scope,
955
1009
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(Collection.Slot, {
@@ -964,7 +1018,13 @@ function createBaseMenu({
964
1018
  disableOutsideScroll: !1,
965
1019
  trapFocus: !1,
966
1020
  onOpenAutoFocus: event => {
967
- rootContext.isUsingKeyboardRef.current && document.querySelector("[data-tamagui-menu-content][data-side]")?.focus(), event.preventDefault();
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();
968
1028
  },
969
1029
  onCloseAutoFocus: event => event.preventDefault(),
970
1030
  onFocusOutside: (0, import_web.composeEventHandlers)(props.onFocusOutside, event => {
@@ -1047,6 +1107,7 @@ function getCheckedState(checked) {
1047
1107
  function focusFirst(candidates, options) {
1048
1108
  const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
1049
1109
  for (const candidate of candidates) if (candidate === PREVIOUSLY_FOCUSED_ELEMENT || (candidate.focus({
1110
+ preventScroll: !0,
1050
1111
  focusVisible: options?.focusVisible
1051
1112
  }), document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT)) return;
1052
1113
  }
@@ -53,6 +53,11 @@ var import_jsx_runtime = require("react/jsx-runtime"),
53
53
  React = __toESM(require("react"), 1),
54
54
  import_react = require("react"),
55
55
  import_MenuPredefined = require("./MenuPredefined.native.js");
56
+ function _type_of(obj) {
57
+ "@swc/helpers - typeof";
58
+
59
+ return obj && typeof Symbol < "u" && obj.constructor === Symbol ? "symbol" : typeof obj;
60
+ }
56
61
  function whenMouse(handler) {
57
62
  return function (event) {
58
63
  return event.pointerType === "mouse" ? handler(event) : void 0;
@@ -94,19 +99,29 @@ function createBaseMenu(param) {
94
99
  Label: _Label = import_MenuPredefined.MenuPredefined.MenuLabel
95
100
  } = param,
96
101
  MenuComp = function (props) {
97
- var {
102
+ var direction = (0, import_use_direction.useDirection)(props.dir),
103
+ defaultPlacement = direction === "rtl" ? "bottom-end" : "bottom-start",
104
+ {
98
105
  scope = MENU_CONTEXT,
99
106
  open = !1,
100
107
  children,
101
108
  dir,
102
109
  onOpenChange,
103
110
  modal = !0,
111
+ allowFlip = {
112
+ padding: 10
113
+ },
114
+ stayInFrame = {
115
+ padding: 10
116
+ },
117
+ placement = defaultPlacement,
118
+ resize = !0,
119
+ offset = 10,
104
120
  ...rest
105
121
  } = props,
106
122
  [content, setContent] = React.useState(null),
107
123
  isUsingKeyboardRef = React.useRef(!1),
108
- handleOpenChange = (0, import_use_callback_ref.useCallbackRef)(onOpenChange),
109
- direction = (0, import_use_direction.useDirection)(dir);
124
+ handleOpenChange = (0, import_use_callback_ref.useCallbackRef)(onOpenChange);
110
125
  return import_web.isWeb && React.useEffect(function () {
111
126
  var handleKeyDown = function () {
112
127
  isUsingKeyboardRef.current = !0, document.addEventListener("pointerdown", handlePointer, {
@@ -133,6 +148,12 @@ function createBaseMenu(param) {
133
148
  };
134
149
  }, []), /* @__PURE__ */(0, import_jsx_runtime.jsx)(PopperPrimitive.Popper, {
135
150
  scope,
151
+ open,
152
+ placement,
153
+ allowFlip,
154
+ stayInFrame,
155
+ resize,
156
+ offset,
136
157
  ...rest,
137
158
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(MenuProvider, {
138
159
  scope,
@@ -229,6 +250,7 @@ function createBaseMenu(param) {
229
250
  type: "presence",
230
251
  present: isPresent,
231
252
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_portal.Portal, {
253
+ stackZIndex: !0,
232
254
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, {
233
255
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(PortalProvider, {
234
256
  scope,
@@ -256,7 +278,10 @@ function createBaseMenu(param) {
256
278
  Provider: MenuContentProvider,
257
279
  useStyledContext: useMenuContentContext
258
280
  } = (0, import_web.createStyledContext)(),
259
- MenuContent = /* @__PURE__ */React.forwardRef(function (props, forwardedRef) {
281
+ MenuContentFrame = (0, import_web.styled)(PopperPrimitive.PopperContentFrame, {
282
+ name: CONTENT_NAME
283
+ }),
284
+ MenuContent = MenuContentFrame.styleable(function (props, forwardedRef) {
260
285
  var scope = props.scope || MENU_CONTEXT,
261
286
  portalContext = usePortalContext(scope),
262
287
  {
@@ -345,6 +370,7 @@ function createBaseMenu(param) {
345
370
  getItems = useCollection(scope),
346
371
  [currentItemId, setCurrentItemId] = React.useState(null),
347
372
  contentRef = React.useRef(null),
373
+ focusableContentRef = React.useRef(null),
348
374
  composedRefs = (0, import_web.useComposedRefs)(forwardedRef, contentRef, context.onContentChange),
349
375
  timerRef = React.useRef(0),
350
376
  searchRef = React.useRef(""),
@@ -383,9 +409,22 @@ function createBaseMenu(param) {
383
409
  return clearTimeout(timerRef.current);
384
410
  };
385
411
  }, []), React.useEffect(function () {
412
+ if (!(!import_web.isWeb || !context.open)) {
413
+ var frame = requestAnimationFrame(function () {
414
+ var container = contentRef.current,
415
+ el = container?.querySelector("[data-tamagui-menu-content]");
416
+ el && (focusableContentRef.current = el);
417
+ });
418
+ return function () {
419
+ return cancelAnimationFrame(frame);
420
+ };
421
+ }
422
+ }, [context.open]), React.useEffect(function () {
386
423
  if (!(!import_web.isWeb || disableDismissOnScroll || !context.open)) {
387
- var handleScroll = function () {
388
- onDismiss?.();
424
+ var handleScroll = function (event) {
425
+ var _contentRef_current,
426
+ target = event.target;
427
+ !((_contentRef_current = contentRef.current) === null || _contentRef_current === void 0) && _contentRef_current.contains(target) || onDismiss?.();
389
428
  };
390
429
  return window.addEventListener("scroll", handleScroll, {
391
430
  capture: !0,
@@ -406,8 +445,11 @@ function createBaseMenu(param) {
406
445
  }, []),
407
446
  content = /* @__PURE__ */(0, import_jsx_runtime.jsx)(PopperPrimitive.PopperContent, {
408
447
  role: "menu",
448
+ // tabIndex allows the content to be focusable so that onItemLeave can
449
+ // focus the content frame and properly blur the previously focused item
450
+ tabIndex: -1,
451
+ unstyled,
409
452
  ...(!unstyled && {
410
- padding: 4,
411
453
  backgroundColor: "$background",
412
454
  borderWidth: 1,
413
455
  borderColor: "$borderColor",
@@ -472,8 +514,8 @@ function createBaseMenu(param) {
472
514
  isPointerMovingToSubmenu(event) && event.preventDefault();
473
515
  }, [isPointerMovingToSubmenu]),
474
516
  onItemLeave: React.useCallback(function (event) {
475
- var _contentRef_current;
476
- isPointerMovingToSubmenu(event) || ((_contentRef_current = contentRef.current) === null || _contentRef_current === void 0 || _contentRef_current.focus(), setCurrentItemId(null));
517
+ var _focusableContentRef_current;
518
+ isPointerMovingToSubmenu(event) || ((_focusableContentRef_current = focusableContentRef.current) === null || _focusableContentRef_current === void 0 || _focusableContentRef_current.focus(), setCurrentItemId(null));
477
519
  }, [isPointerMovingToSubmenu]),
478
520
  onTriggerLeave: React.useCallback(function (event) {
479
521
  isPointerMovingToSubmenu(event) && event.preventDefault();
@@ -490,7 +532,9 @@ function createBaseMenu(param) {
490
532
  onMountAutoFocus: (0, import_web.composeEventHandlers)(onOpenAutoFocus, function (event) {
491
533
  event.preventDefault();
492
534
  var content2 = document.querySelector("[data-tamagui-menu-content]");
493
- content2?.focus();
535
+ content2?.focus({
536
+ preventScroll: !0
537
+ });
494
538
  }),
495
539
  onUnmountAutoFocus: onCloseAutoFocus,
496
540
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_dismissable.Dismissable, {
@@ -522,10 +566,11 @@ function createBaseMenu(param) {
522
566
  MenuContent.displayName = CONTENT_NAME;
523
567
  var ITEM_NAME = "MenuItem",
524
568
  ITEM_SELECT = "menu.itemSelect",
525
- MenuItem = /* @__PURE__ */React.forwardRef(function (props, forwardedRef) {
569
+ MenuItem = _Item.styleable(function (props, forwardedRef) {
526
570
  var {
527
571
  disabled = !1,
528
572
  onSelect,
573
+ preventCloseOnSelect,
529
574
  children,
530
575
  scope = MENU_CONTEXT,
531
576
  // filter out native-only props that shouldn't reach the DOM
@@ -556,10 +601,10 @@ function createBaseMenu(param) {
556
601
  return onSelect?.(event);
557
602
  }, {
558
603
  once: !0
559
- }), (0, import_dismissable.dispatchDiscreteCustomEvent)(menuItemEl, itemSelectEvent), itemSelectEvent.defaultPrevented ? isPointerDownRef.current = !1 : rootContext.onClose();
604
+ }), (0, import_dismissable.dispatchDiscreteCustomEvent)(menuItemEl, itemSelectEvent), itemSelectEvent.defaultPrevented || preventCloseOnSelect ? isPointerDownRef.current = !1 : rootContext.onClose();
560
605
  } else onSelect?.({
561
606
  target: menuItem
562
- }), isPointerDownRef.current = !1, rootContext.onClose();
607
+ }), isPointerDownRef.current = !1, preventCloseOnSelect || rootContext.onClose();
563
608
  },
564
609
  content = typeof children == "string" ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_web.Text, {
565
610
  children
@@ -621,12 +666,8 @@ function createBaseMenu(param) {
621
666
  asChild: !0,
622
667
  __scopeRovingFocusGroup: scope,
623
668
  focusable: !disabled,
624
- ...(!unstyled && {
625
- flexDirection: "row",
626
- alignItems: "center"
627
- }),
628
- ...itemProps,
629
669
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(_Item, {
670
+ unstyled,
630
671
  componentName: ITEM_NAME,
631
672
  role: "menuitem",
632
673
  "data-highlighted": isFocused ? "" : void 0,
@@ -723,7 +764,7 @@ function createBaseMenu(param) {
723
764
  });
724
765
  MenuItemIcon.displayName = ITEM_ICON;
725
766
  var CHECKBOX_ITEM_NAME = "MenuCheckboxItem",
726
- MenuCheckboxItem = /* @__PURE__ */React.forwardRef(function (props, forwardedRef) {
767
+ MenuCheckboxItem = _Item.styleable(function (props, forwardedRef) {
727
768
  var {
728
769
  checked = !1,
729
770
  onCheckedChange,
@@ -781,7 +822,7 @@ function createBaseMenu(param) {
781
822
  });
782
823
  MenuRadioGroup.displayName = RADIO_GROUP_NAME;
783
824
  var RADIO_ITEM_NAME = "MenuRadioItem",
784
- MenuRadioItem = /* @__PURE__ */React.forwardRef(function (props, forwardedRef) {
825
+ MenuRadioItem = _Item.styleable(function (props, forwardedRef) {
785
826
  var {
786
827
  value,
787
828
  scope = MENU_CONTEXT,
@@ -842,6 +883,7 @@ function createBaseMenu(param) {
842
883
  return /* @__PURE__ */(0, import_jsx_runtime.jsx)(PopperPrimitive.PopperArrow, {
843
884
  scope,
844
885
  componentName: "PopperArrow",
886
+ unstyled,
845
887
  ...(!unstyled && {
846
888
  backgroundColor: "$background"
847
889
  }),
@@ -855,19 +897,43 @@ function createBaseMenu(param) {
855
897
  useStyledContext: useMenuSubContext
856
898
  } = (0, import_web.createStyledContext)(),
857
899
  MenuSub = function (props) {
858
- var {
859
- scope = MENU_CONTEXT,
900
+ var _parentPopperContext_placement,
901
+ isTouchDevice = (0, import_web.useIsTouchDevice)(),
902
+ {
903
+ scope = MENU_CONTEXT
904
+ } = props,
905
+ rootContext = useMenuRootContext(scope),
906
+ parentPopperContext = PopperPrimitive.usePopperContext(scope),
907
+ parentSide = (_parentPopperContext_placement = parentPopperContext.placement) === null || _parentPopperContext_placement === void 0 ? void 0 : _parentPopperContext_placement.split("-")[0],
908
+ isNestedSubmenu = parentSide === "left" || parentSide === "right",
909
+ defaultPlacement = isTouchDevice ? "bottom" : isNestedSubmenu ? `${parentSide}-start` : rootContext.dir === "rtl" ? "left-start" : "right-start",
910
+ {
860
911
  children,
861
912
  open = !1,
862
913
  onOpenChange,
863
- allowFlip = {
914
+ allowFlip: allowFlipProp = {
864
915
  padding: 10
865
916
  },
866
917
  stayInFrame = {
867
918
  padding: 10
868
919
  },
920
+ placement = defaultPlacement,
869
921
  ...rest
870
922
  } = props,
923
+ allowFlip = React.useMemo(function () {
924
+ if (!isNestedSubmenu || typeof allowFlipProp == "boolean" || allowFlipProp.fallbackPlacements) return allowFlipProp;
925
+ var side = placement.split("-")[0],
926
+ align = placement.split("-")[1] || "start",
927
+ otherAlign = align === "start" ? "end" : "start";
928
+ if (side === "left" || side === "right") {
929
+ var oppositeSide = side === "right" ? "left" : "right";
930
+ return {
931
+ ...((typeof allowFlipProp > "u" ? "undefined" : _type_of(allowFlipProp)) === "object" ? allowFlipProp : {}),
932
+ fallbackPlacements: [`${side}-${otherAlign}`, `${oppositeSide}-${align}`, `${oppositeSide}-${otherAlign}`]
933
+ };
934
+ }
935
+ return allowFlipProp;
936
+ }, [isNestedSubmenu, allowFlipProp, placement]),
871
937
  parentMenuContext = useMenuContext(scope),
872
938
  [trigger, setTrigger] = React.useState(null),
873
939
  [content, setContent] = React.useState(null),
@@ -877,6 +943,8 @@ function createBaseMenu(param) {
877
943
  return handleOpenChange(!1);
878
944
  };
879
945
  }, [parentMenuContext.open, handleOpenChange]), /* @__PURE__ */(0, import_jsx_runtime.jsx)(PopperPrimitive.Popper, {
946
+ open,
947
+ placement,
880
948
  allowFlip,
881
949
  stayInFrame,
882
950
  ...rest,
@@ -901,8 +969,7 @@ function createBaseMenu(param) {
901
969
  MenuSub.displayName = SUB_NAME;
902
970
  var SUB_TRIGGER_NAME = "MenuSubTrigger",
903
971
  MenuSubTrigger = /* @__PURE__ */React.forwardRef(function (props, forwardedRef) {
904
- var _popperContext_placement,
905
- scope = props.scope || MENU_CONTEXT,
972
+ var scope = props.scope || MENU_CONTEXT,
906
973
  context = useMenuContext(scope),
907
974
  rootContext = useMenuRootContext(scope),
908
975
  subContext = useMenuSubContext(scope),
@@ -913,8 +980,7 @@ function createBaseMenu(param) {
913
980
  pointerGraceTimerRef,
914
981
  onPointerGraceIntentChange
915
982
  } = contentContext,
916
- placementSide = (_popperContext_placement = popperContext.placement) === null || _popperContext_placement === void 0 ? void 0 : _popperContext_placement.split("-")[0],
917
- effectiveDir = placementSide === "left" ? "rtl" : placementSide === "right" ? "ltr" : rootContext.dir,
983
+ effectiveDir = rootContext.dir,
918
984
  clearOpenTimer = React.useCallback(function () {
919
985
  openTimerRef.current && window.clearTimeout(openTimerRef.current), openTimerRef.current = null;
920
986
  }, []);
@@ -995,9 +1061,9 @@ function createBaseMenu(param) {
995
1061
  var triggerEl = subContext.trigger,
996
1062
  triggerRect = triggerEl?.getBoundingClientRect();
997
1063
  if (triggerRect) {
998
- var _popperContext_placement2,
999
- placementSide2 = (_popperContext_placement2 = popperContext.placement) === null || _popperContext_placement2 === void 0 ? void 0 : _popperContext_placement2.split("-")[0],
1000
- side1 = placementSide2 === "left" || placementSide2 === "right" ? placementSide2 : rootContext.dir === "rtl" ? "left" : "right",
1064
+ var _popperContext_placement,
1065
+ placementSide = (_popperContext_placement = popperContext.placement) === null || _popperContext_placement === void 0 ? void 0 : _popperContext_placement.split("-")[0],
1066
+ side1 = placementSide === "left" || placementSide === "right" ? placementSide : rootContext.dir === "rtl" ? "left" : "right",
1001
1067
  rightSide1 = side1 === "right",
1002
1068
  bleed1 = rightSide1 ? -5 : 5,
1003
1069
  nearEdge = rightSide1 ? triggerRect.right + 4 : triggerRect.left - 4,
@@ -1064,7 +1130,10 @@ function createBaseMenu(param) {
1064
1130
  });
1065
1131
  MenuSubTrigger.displayName = SUB_TRIGGER_NAME;
1066
1132
  var SUB_CONTENT_NAME = "MenuSubContent",
1067
- MenuSubContent = /* @__PURE__ */React.forwardRef(function (props, forwardedRef) {
1133
+ MenuSubContentFrame = (0, import_web.styled)(PopperPrimitive.PopperContentFrame, {
1134
+ name: SUB_CONTENT_NAME
1135
+ }),
1136
+ MenuSubContent = MenuSubContentFrame.styleable(function (props, forwardedRef) {
1068
1137
  var _popperContext_placement,
1069
1138
  scope = props.scope || MENU_CONTEXT,
1070
1139
  portalContext = usePortalContext(scope),
@@ -1080,7 +1149,7 @@ function createBaseMenu(param) {
1080
1149
  composedRefs = (0, import_web.useComposedRefs)(forwardedRef, ref),
1081
1150
  placementSide = (_popperContext_placement = popperContext.placement) === null || _popperContext_placement === void 0 ? void 0 : _popperContext_placement.split("-")[0],
1082
1151
  dataSide = placementSide === "left" || placementSide === "right" ? placementSide : rootContext.dir === "rtl" ? "left" : "right",
1083
- effectiveDir = placementSide === "left" ? "rtl" : placementSide === "right" ? "ltr" : rootContext.dir;
1152
+ effectiveDir = rootContext.dir;
1084
1153
  return /* @__PURE__ */(0, import_jsx_runtime.jsx)(Collection.Provider, {
1085
1154
  scope,
1086
1155
  children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(Collection.Slot, {
@@ -1096,8 +1165,13 @@ function createBaseMenu(param) {
1096
1165
  trapFocus: !1,
1097
1166
  onOpenAutoFocus: function (event) {
1098
1167
  if (rootContext.isUsingKeyboardRef.current) {
1099
- var content = document.querySelector("[data-tamagui-menu-content][data-side]");
1100
- content?.focus();
1168
+ var _root_querySelector,
1169
+ _this,
1170
+ root = ref.current,
1171
+ content = root == null || (_root_querySelector = root.querySelector) === null || _root_querySelector === void 0 ? void 0 : _root_querySelector.call(root, "[data-tamagui-menu-content]");
1172
+ (_this = content || root) === null || _this === void 0 || _this.focus({
1173
+ preventScroll: !0
1174
+ });
1101
1175
  }
1102
1176
  event.preventDefault();
1103
1177
  },
@@ -1212,6 +1286,7 @@ function focusFirst(candidates, options) {
1212
1286
  for (var _iterator = candidates[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = !0) {
1213
1287
  var candidate = _step.value;
1214
1288
  if (candidate === PREVIOUSLY_FOCUSED_ELEMENT || (candidate.focus({
1289
+ preventScroll: !0,
1215
1290
  focusVisible: options?.focusVisible
1216
1291
  }), document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT)) return;
1217
1292
  }