Giter Site home page Giter Site logo

react-native-popable's Introduction

react-native-popable

Popovers, tooltips for React Native.

🤖 Controlled and uncontrolled popovers

✍️ Customizable popover content (text, views)

🌐 Works on web, hover support

🙅‍♂️ No-dependency

🎒 Built with Typescript

👩‍🔬 Try the API sandbox

<Popable content="See profile">
  <Text>@eveningkid</Text>
</Popable>

Usage

npm install react-native-popable

If working with React Native Web, you'll need at least version 0.15.0. It introduced hover events for Pressable which is used internally.

Popable

Add a popover around a given component. Uses Popover internally.

Every property coming from Popover can be used the exact same way that with Popable.

import { Popable } from 'react-native-popable';

export default () => (
  <Popable content="See profile">
    <Text>@morning_cafe</Text>
  </Popable>
);

Popable.children

What should serve as the popover trigger, basically any React element.

<Popable content="See profile">
  <Image
    source={{ uri: ... }}
    style={{ width: 50, height: 50 }}
  />
</Popable>

<Popable content="See profile">
  <Text>@morning_cafe</Text>
</Popable>

content

Popover content: can be a string or any React element (text, views).

If you just want the popover, without all the outside logic that comes from Popable, use Popover instead.

<Popable
  content={
    <View
      style={{
        padding: 10,
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: 'white'
      }}
    >
      <Text>Anything :)</Text>
    </View>
  }
>
  <Text>@morning_cafe</Text>
</Popable>

<Popable content="See profile">
  <Text>@morning_cafe</Text>
</Popable>

action

Upon what action should the popover be shown/dismissed: press, longpress or hover (web-only). Defaults to press.

<Popable action="hover" content="See profile">
  <Text>@morning_cafe</Text>
</Popable>

onAction

Callback to monitor the popover visibility state. Called whenever visible changes (even through Popover internal state). Useful for side-effects.

<Popable
  onAction={(visible) => {
    if (visible) {
      Analytics.pressedProfilePopover();
    }
  }}
  content="See profile"
>
  <Text>@morning_cafe</Text>
</Popable>

strictPosition

If the popover should be placed on the opposite side when it doesn't fit at the given position. If a popover is on the left of the screen and its position is left, the position will be turned to right by default. If strictPosition is true, the popover will remain on the left. Defaults to false.

<Popable strictPosition={true} position="left">
  @morning_cafe
</Popable>

style

Style the Popover component using any View style property.

<Popable style={{ opacity: 0.8 }}>@morning_cafe</Popable>

wrapperStyle

Style the wrapping View component using any View style property.

<Popable wrapperStyle={{ flex: 1, display: 'flex' }}>@morning_cafe</Popable>

Popover

The UI component in Popable can also be used on its own.

import { Popover } from 'react-native-popable';

export default () => <Popover>@morning_cafe</Popover>;

children

The popover content: will render text if a string is given or the given React elements otherwise.

<Popover>@morning_cafe</Popover>

<Popover>
  <Image
    source={{ uri: ... }}
    style={{ width: 50, height: 50 }}
  />
</Popover>

animated

If the popover should animate when the visible property changes. Defaults to true.

<Popover animated={false}>@morning_cafe</Popover>

animationType

If the popover should bounce a little (spring) or not (timing). Defaults to timing.

<Popover animationType="spring">@morning_cafe</Popover>

backgroundColor

Background color for the popover and the caret.

<Popover backgroundColor="red">@morning_cafe</Popover>

caret

If the little caret (the "half-triangle") should be displayed. Defaults to true.

<Popover caret={false}>@morning_cafe</Popover>

caretPosition

Position for the caret: left, center or right. Defaults to center.

<Popover caretPosition="right">@morning_cafe</Popover>

forceInitialAnimation

If the popover should animate when it renders for the first time. This means that if visible is set to true, the popover will fade in after it mounted. Likewise, if visible is false, the popover will fade out. If this property is kept falsy, the popover will be displayed in its initial visibility state, without animating. It is very unlikely you would ever need this property. Defaults to false.

<Popover forceInitialAnimation>@morning_cafe</Popover>

numberOfLines

Limit the number of lines if children is a string. Corresponds to Text.numberOfLines which clips text with ... if the given text is more than a number of lines.

<Popover numberOfLines={1}>@morning_cafe_got_longer</Popover>

visible

If the popover should be visible. Will animate every value change if animated is true.

const [visible, setVisible] = useState(false);

<Popover visible={visible}>@morning_cafe</Popover>

<Button
  title="Toggle visibility"
  onPress={() => {
    setVisible((isVisible) => !isVisible);
  }}
/>

position

Position for the popover: top, right, bottom or left. Changes the caret position. Defaults to top.

<Popover position="right">@morning_cafe</Popover>

ViewProps

Every usual View property is available as well.

<Popover onLayout={...}>@morning_cafe</Popover>

usePopable

If you need to imperatively control the Popable component, you can use the usePopable hook. It lets you show and hide the Popable without needing to manage state yourself.

You typically won't need to use this hook, since react-native-popable intelligently hides popovers when users press or hover away. However, it comes in handy for features like menus.

Usage

const [ref, { hide, show }] = usePopable();

return <Popable ref={ref} />

If you prefer to not use the array syntax, you can destructure like so:

const { ref, hide, show } = usePopable();

return <Popable ref={ref} />

Hide the Popable

If you're building a Popable menu, you'll want to hide the Popable when someone clicks a menu item.

import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import { Popable, usePopable } from 'react-native-popable';

import Menu from './menu';

export default function App() {
  const [ref, { hide }] = usePopable();

  return (
    <View style={styles.container}>
      <Popable ref={ref} content={<Menu onPressMenuItem={() => hide()} />}>
        <Text>Open Menu</Text>
      </Popable>
    </View>
  );
}

Show the Popable

Similar to the example above, you can show the Popable imperatively:

import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import { Popable, usePopable } from 'react-native-popable';

import Menu from './menu';

export default function App() {
  const [ref, { show, hide }] = usePopable();

  return (
    <View style={styles.container}>
      <Button title="Open Menu" onPress={() => show()} />

      <Popable ref={ref} content={<Menu onPressMenuItem={() => hide()} />}>
        <Text>Menu</Text>
      </Popable>
    </View>
  );
}

This is a rare use-case, since you'll typically use the children as the trigger of your Popable.

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT © eveningkid

react-native-popable's People

Contributors

eveningkid avatar fredrivett avatar nandorojo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-native-popable's Issues

Live demo?

Do you have a live demo somewhere to link to? I don't see one in the docs.

Popable not working in landscape view in iOS

Issue scenario: When the device is in landscape mode click on popable element, it automatically rotates your screen to portrait and display the popable
For android it's working fine

Idea: auto placement

I think it would be useful to re-align the popover if the proposed alignment doesn't fit within the screen.

I envision something like this:

Popable alignment

The first situation would actually render the second one. However, if you pass something like strictPlacement, then it doesn't do that.

Let me know what you think. Really excited about this project. Popovers are an important addition to RN, as I mentioned here. I'm glad to see it's being solved.

onAction doesn't get called when Popable disappears.

I am trying to change the state variable on the tap of the tooltip component.
Have a look at the code snippet below.

<> <Popable onAction={visible => { setIsSelected(visible); }} content={ <View style={styles.tooltipView}> <Text style={styles.font}>Anything :)</Text> </View> }> <View style={styles.infoIcon}> {isSelected ? ( <Image source={require('assets/images/tool_tip_info_black.png')} /> ) : ( <Image source={require('assets/images/tool_tip_info.png')} /> )} </Pressable> </Popable> </>

I believe visible should provide me the state of the popable. But that doesn't seem to be happening. Kindly help me out.

CaretPosition left or right positioning incorrect

v0.4.3

When I put caretPosition={"right"} , the caret does not point to correct position

Screenshot 2021-10-11 at 10 50 41 AM

<Popable
      // style={{ width: 300, flex: 1, display: "flex" }}
      backgroundColor={palette.aia_light_gold}
      caretPosition={"right"}
      content={
        <View
          style={{
            // flex: 1,
            padding: 10,
            alignItems: "center",
            justifyContent: "center",
          }}
        >
          <Text style={{ color: "black" }}>
            Example tooltip text
          </Text>
        </View>
      }
    >
      <Image
        style={{
          resizeMode: "contain",
          height: typography.fontSize.small,
          width: typography.fontSize.small,
        }}
        source={require("../../assets/icon_info.png")}
      />
    </Popable>

Show popable on tap of image

Hello folks
Thanks for wonderfull library. Can i show popable where i can tap? I need to use for tagging purpose so is it possible to do like that? also can i drag and drop anywhere ?

Not working

Simulator Screen Shot - iPhone 11 - 2021-07-18 at 15 27 03

<Popable
content={
<View
style={{
padding: 10,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'white'
}}
>
{mtdlegend}

}>
<Text style={{
fontFamily:'MaisonNeue-Bold',
fontSize: 12,
color: mtd_color,
textTransform: 'uppercase'
}}>{mtd}

Lengthy content is invisible

Hi Team

As i am trying to add popable tooltip in lengthy way but its is invisible due to the absents of scroll. so can you please add scrollview for the popable content.

`visible={true}` has no effect on Android

I am using the code below from the sandbox example. However, despite visible={true}, the Popable does not show. Am I doing something wrong?

import React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Popable } from 'react-native-popable';

export default function App() {
  return (
    <View style={styles.container}>
      <Popable content="See profile" position="bottom" visible={true}>
        <Text style={{ textAlign: 'center'}}>@morning_cafe</Text>
      </Popable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

Popover misaligned when interacting with the keyboard

The popover becomes misaligned if it is opened whilst the keyboard is open, where pressing the trigger causes the keyboard to close.

Seems to be conflict with the Model used for the backdrop which is causing the keyboard to hide.
Then return once the model closes (probably because I use autoFocus on the TextInput)

Best demonstrated with a screen recording

Screen.Recording.2021-09-16.at.23.51.01.mov

This sounds like similar symptom https://stackoverflow.com/questions/61654984/persist-keyboard-when-showing-a-react-native-modal

Disable tab style/tabindex

When we use the Popable, it adds a div around with a tabindex and outline-style.
It would be nice if this could be disabled

Creating popover menus

Hey, I'm looking to create popover menus with this kind of experience (from Shopify):

https://www.loom.com/share/2ae4312a24dc4a54800a898fe9477219

Some things to note:

  • Has an anchor button which toggles the state
  • Clicking away hides the menu

I would maybe be fine with using hover/focus state to handle it being open, too (it needs to also work on mobile browsers, though). It doesn't necessarily need to be just onPress if that's difficult.

Is something like this possible with the current API?

Added context

Another nice feature you'll see in the video: if you click outside of the menu section onto somewhere else, the click event is still handled at the other spot you pressed. This can be easily achieved with a useClickAway(viewRef) function. This would be web-only behavior, since . That said, I think it could make sense to have in this library. (The clickaway feature would be a cherry on top. It isn't a requirement for the menu behavior described above, so it's fine if you'd like to go without it.)

Curious to hear your thoughts @eveningkid. Thanks again!

PS Another nice example of this is Vercel's menu component, as well as their popover/tooltip.

Popover misaligned when using longpress

The popover aligns to the top left corner of the screen, instead of above the wrapped content when used with action="longpress"

Demonstration of the issue.
Upon long pressing on the blue bubble with the text in, the popover with the content "Copy" appears top left of the screen under the clock

Simulator.Screen.Recording.-.iPhone.11.-.2021-09-16.at.23.00.21.mp4

If I remove the action prop and just use the default single press, the popover aligns correctly above the blue text bubble

Here's a snippet

  const [ref, { hide }] = usePopable();

  const handleCopyPress = () => {
    Clipboard.setString(content);
    onCopied();
    hide();
  }

  const copyPressable = (
    <Pressable onPress={handleCopyPress}>
      <NativeText style={styles.copyText}>Copy</NativeText>
    </Pressable>
  );

  return(
    <View>
      <Popable ref={ref} content={copyPressable} action="longpress">
        {children}
      </Popable>
    </View>
  )

Clipped popable edges for left/right positioning

Hi there, this is friggitydingo, came here from Reddit :P I've been trying out the component, I like it a lot!

When I tried the left/right positions, however, with more several words of text content, I found the edge of the component was clipping off like in the image I attached.

This can be reproduced in your example app with the following:
<Popable content="See profile and tell me what you think" position='left' strictPosition={true}>

I was able to fix this for myself by commenting out the overflow: 'hidden' line in the container style of react-native-popable/src/Popover.tsx - although I'm not sure if that will adversely affect other things.

Thanks!

popable

Imperatively manage popover

Ported from #7: I'd like an API to be able to close the popover without necessarily controlling its state. Imagine you click one of the menu items. It's not a click-away event, but it should close the popover imperatively.

Something like this would be nice:

const popover = useRef(null)

return <Popover ref={popover} content={<Menu onPressItem={() => popover.current?.close()} />} />

This would be simple to implement with useImperativeHandle under the hood.

A custom hook would be a nice touch, too, but not totally necessary. I'd build something like this in my own app if this lib doesn't have one:

const [ref, { close }] = usePopover()

const onPressMenuItem = () => {
  close()
}

return <Popover ref={ref} />

I can look into making this if you'd like.

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.