Coder Social home page Coder Social logo

intergalacticspacehighway / react-native-popper Goto Github PK

View Code? Open in Web Editor NEW
103.0 2.0 3.0 1.44 MB

Helps you create customizable and accessible Popovers / Tooltips with React Native.

Home Page: https://react-native-popper.netlify.app/

License: MIT License

JavaScript 4.27% TypeScript 95.73%
popovers reactnative tooltips

react-native-popper's Issues

Tooltip stays open after clicking trigger an hovering out

I have a action button with a Tooltip on it.

When i click this button and i hover out, the tooltip should close.

Right now, i need to manually click outside the trigger for the tooltip to disapear.

Screen.Recording.2022-11-25.at.11.56.48.AM.mov

Ability to Open a Popover when another one is already open

First off, thank you for making a simple light weight popover component that works on android, iOS, and Web!
I have either a suggestion or a bug, please point me in the right direction. :)

The follow behavior is seen in Web and Android (haven't tested iOS)

Expected Behavior:
When one popover is open, I should be able to click on a trigger of another popover and the app will close the first and open the second

Actual Behavior:
When one popover is open, previously clickable items are not clickable until the popover is closed.

GIF
react-native-popper-andoird

Menu example accessiblity

Any chance you could critique my menu component for accessiblity? Not really familiar with good accessiblity, so I was wondering how to optimize this menu component with props, etc:

import React, {
  ReactNode,
  createContext,
  useContext,
  useMemo,
  useState,
  ComponentProps,
  useRef,
  useImperativeHandle,
} from 'react'

import { View, Text, SxProp, Pressable as Press, Theme as DripsyTheme } from 'dripsy'
import { Popover } from 'react-native-popper'
import { Sizes } from '../types'

type MenuProps = {
  children: ReactNode
  /*
   * Default: `240`
   */
  width?: number
  height?: number
  menu?: React.Ref<MenuRef>
  sx?: SxProp
} & Omit<ComponentProps<typeof Popover>, 'isOpen' | 'children' | 'onOpenChange'>

const MenuVisibleContext = createContext({
  visible: false,
  onClose() {
    //
  },
  onShow() {
    //
  },
  onChange(next: boolean) {
    //
  },
})

const useMenuVisibleContext = () => useContext(MenuVisibleContext)

function MenuProvider({ children }: { children: ReactNode }) {
  const [visible, setVisible] = useState(false)

  return (
    <MenuVisibleContext.Provider
      value={useMemo(
        () => ({
          visible,
          onShow: () => setVisible(true),
          onClose: () => setVisible(false),
          onChange: setVisible,
        }),
        [visible]
      )}
    >
      {children}
    </MenuVisibleContext.Provider>
  )
}
 
const Menu = function Menu(props: MenuProps) {
  return (
    <MenuProvider>
      <MenuWithContext {...props} />
    </MenuProvider>
  )
}

function MenuDivider() {
  return <View sx={{ height: 1, width: '100%', bg: 'mutedText', my: 2 }} />
}

function MenuWithContext({
  children,
  width = 240,
  height,
  menu,
  ...props
}: MenuProps) {
  const { visible, onChange } = useMenuVisibleContext()

  useImperativeHandle(menu, () => ({
    show: () => onChange(true),
    close: () => onChange(false),
  }))

  return (
    <Popover offset={2} {...props} isOpen={visible} onOpenChange={onChange}>
      <Popover.Backdrop />
      <Popover.Content>
        <View
          sx={{
            width,
            height,
            borderWidth: 1,
            bg: 'background',
            borderRadius: 3,
            borderColor: 'mutedText',
            py: 2,
          }}
        >
          {children}
        </View>
      </Popover.Content>
    </Popover>
  )
}

type MenuItemProps = {
  children: string | ReactNode
  onPress?: () => void
  prefix?: ReactNode
  suffix?: ReactNode
  disabled?: boolean
  color?: keyof DripsyTheme['colors']
  /*
   * default: `true`
   */
  shouldCloseOnPress?: boolean
}

function MenuItem({
  children,
  onPress,
  prefix,
  suffix,
  disabled = false,
  color,
  shouldCloseOnPress = true,
}: MenuItemProps) {
  const { onClose } = useMenuVisibleContext()

  const composePress = () => {
    onPress?.()
    if (shouldCloseOnPress) {
      onClose()
    }
  }

  return (
    <Press disabled={disabled} onPress={composePress}>
      {({ hovered, pressed }) => {
        return (
          <View
            sx={{
              px: 3,
              py: 2,
              bg: hovered || pressed ? 'muted2' : undefined,
              flexDirection: 'row',
              alignItems: 'center',
              opacity: disabled ? 0.7 : 1,
              cursor: disabled ? 'not-allowed' : 'pointer',
            }}
          >
            {!!prefix && <View sx={{ mr: 2 }}>{prefix}</View>}
            <View sx={{ flex: 1 }}>
              <Text
                sx={{
                  color: color ?? (hovered || pressed ? 'text' : 'muted7'),
                }}
              >
                {children}
              </Text>
            </View>
            {!!suffix && <View sx={{ ml: 2 }}>{suffix}</View>}
          </View>
        )
      }}
    </Press>
  )
}

type MenuSectionProps = {
  children: ReactNode
  title: string
}

function MenuSection({ children, title }: MenuSectionProps) {
  return (
    <View>
      <Text sx={{ py: 2, px: 3, color: 'mutedText' }}>{title}</Text>
      {children}
    </View>
  )
}

type MenuTriggerProps = {
  children: ReactNode
  onPress?: never
} & (
  | {
      unstyled?: true
    }
  | ({
      unstyled?: false
    } & ComponentProps<typeof Button>)
)

const TriggerContext = React.createContext(false)

const MenuTrigger = React.forwardRef(function MenuTrigger(
  { unstyled = true, ...props }: MenuTriggerProps,
  ref
) {
  let node = <Button ref={ref} {...props} />
  if (unstyled) {
    node = <Press ref={ref} {...(props as any)} />
  }

  return <TriggerContext.Provider value={true}>{node}</TriggerContext.Provider>
})

type MenuIconButtonProps = {
  size?: Sizes
  shape?: 'square' | 'circle' | 'none'
  icon: IconProps['icon']
  onPress?: never // internal usage only
}
 
Menu.Trigger = MenuTrigger
Menu.Section = MenuSection
Menu.Item = MenuItem
Menu.Divider = MenuDivider

export { Menu }

Next.js support

Hey there, I think there's some sort of issue with a dependency. I'm trying to import this in my Next.js app, and I'm seeing this:

Screen Shot 2021-07-23 at 6 12 14 PM

Any idea what that might be?

When running yarn why dom-helpers, I get this:

=> Found "[email protected]"
info Reasons this module exists
   - "_project_#@beatgig#components#@material-ui#pickers#react-transition-group" depends on it
   - Hoisted from "_project_#@beatgig#components#@material-ui#pickers#react-transition-group#dom-helpers"
info Disk size without dependencies: "984KB"
info Disk size with unique dependencies: "2.76MB"
info Disk size with transitive dependencies: "2.8MB"
info Number of shared dependencies: 3
=> Found "@react-aria/overlays#[email protected]"
info This module exists because "_project_#expo-next-app#react-native-popper#@react-native-aria#overlays#@react-aria#overlays" depends on it.
info Disk size without dependencies: "932KB"
info Disk size with unique dependencies: "1.6MB"
info Disk size with transitive dependencies: "1.64MB"
info Number of shared dependencies: 2
✨  Done in 1.34s.

Add customizable `Modal` component from react-native-screens

Now that react-native-screens has an overlay view that allows multiple overlays on iOS (and in the future Android), it would be great if this lib exposed a way to pass a custom Modal component. I envision something roughly like this:

const CustomModal = (props) => {
  if (Platform.OS !== 'ios') {
    return <Modal {...props} />
  }

  const { visible, children } = props

  return <OverlayView style={StyleSheet.absoluteFill}>{visible && children}</OverlayView>
}

return <Popover Modal={CustomModal} />

Even better, what if this library had a plugin, like react-native-popper/window, such that all you had to do is this:

import { WindowOverlay } from 'react-native-popper/window'

<Popover Modal={WindowOverlay} />

This way, only users of react-native-popper/window need to install react-native-screens.

Trouble dismissing the modal when outside Pressable component is pressed on web.

I'm running into some trouble when trying to dismiss the modal when a pressable is pressed outside of the modal on web. The modal currently works as expected when pressing outside of the popper if the element isn't another pressable, however when pressing another pressable e.i a button, the popper stays open.

Any guidance on this would be great.

Add `onDismiss` prop

If we could just forward onDismiss down to the modal, then I could refrain from opening other modals until this one is hidden.

Custom animations

Is there any way to customize the animation config? Or if not, could I provide a function as a child to do my own (such as with Moti)? I'd like to fade in down.

Modal Support

Hey guys, I'm trying to use this package currently, I've hit a little bit of a problem tho, when using it in conjunction with the react native modal it causes the popped item to appear below it instead of on top.
Is there any way that behaviour could be changed? If so I'd appreciate it a lot!

Thanks for the amazing package,
Luis Bizarro

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.