nandorojo / zeego

Menus for React (Native) done right.
https://zeego.dev
MIT License
1.42k stars 41 forks source link

Z-index issue on web in modal #74

Closed HellYeahOmg closed 4 months ago

HellYeahOmg commented 6 months ago

I'm getting an issue with dropdown menus on react native for web - they are hidden under the default react native modal component and I can't find a workaround for it.

The issue is that radix popper component sets z-index as auto:

image

The code is really simple:

export function HomePage() {
  const [modalVisible, setModalVisible] = useState(false)

  return (
    <View>
      <Modal visible={modalVisible} transparent={false}>
        <View
          style={{
            marginBottom: 40,
          }}
        >
          <Typography>
            Lorem ipsum dolor sit amet, consectetur adipisicing elit.
            Perferendis, quaerat, quisquam? Autem dicta laudantium maxime minus,
            nam nihil, optio quas, quasi quos rem sed vel voluptates. Aut
            commodi saepe voluptas?
          </Typography>
        </View>

        <DropdownMenuRoot>
          <DropdownMenuTrigger>
            <Typography>menu?</Typography>
          </DropdownMenuTrigger>
          <DropdownMenuContent>
            <DropdownMenuItem key="fernando rojo">
              <DropdownMenuItemTitle>Fernando Rojo</DropdownMenuItemTitle>
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenuRoot>
      </Modal>

      <Pressable onPress={() => setModalVisible(true)}>show modal</Pressable>
    </View>
  )
}
nandorojo commented 4 months ago

Yeah, RNW's modal is incompatible with radix. I use this as my modal replacement on web when it has a modal in it. And I pass plainDiv as a prop to my modal.

import { Modal as NativeModal, Platform as platform, View } from 'react-native'
import { createPortal } from 'react-dom'
import { useEffect, useRef } from 'react'

export const Modal = function Modal({
  presentationStyle = platform.select({ ios: 'formSheet' }),
  animationType = platform.select({ native: 'slide' }),
  transparent = new Set(['formSheet', 'fullScreen']).has(presentationStyle || '')
    ? undefined
    : true,
  plainDiv,
  ...props
}: Props) {
  if (plainDiv && platform.OS === 'web') {
    // eslint-disable-next-line react-hooks/rules-of-hooks
    const elementRef = useRef<HTMLDivElement | null>(null)
    if (typeof window !== 'undefined' && !elementRef.current) {
      const element = document.createElement('div')

      if (element && document.body) {
        document.body.appendChild(element)
        elementRef.current = element
      }
    }

    // eslint-disable-next-line react-hooks/rules-of-hooks
    useEffect(function cleanup() {
      return () => {
        if (document.body && elementRef.current) {
          document.body.removeChild(elementRef.current)
          elementRef.current = null
        }
      }
    }, [])

    // for radix menus, which glitch a lot with regular modals on RNW
    if (!props.visible) return null
    const node = (
      <View
        style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, zIndex: 100 }}
        onClick={(e) => {
          e.stopPropagation()
        }}
      >
        {props.children}
      </View>
    )
    return elementRef.current ? createPortal(node, elementRef.current) : null
  }

  return (
    <NativeModal
      {...props}
      transparent={
        platform.OS == 'ios' && presentationStyle == 'formSheet' ? undefined : transparent
      }
      presentationStyle={presentationStyle}
      animationType={animationType}
    >
      {props.children}
    </NativeModal>
  )
}