Giter Club home page Giter Club logo

react-useportal's Introduction

usePortal

🌀 React hook for using Portals

undefined undefined undefined Known Vulnerabilities Known Vulnerabilities

Need to make dropdowns, lightboxes/modals/dialogs, global message notifications, or tooltips in React? React Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component (react docs).

This hook is also isomorphic, meaning it works with SSR (server side rendering).

Features

  • SSR (server side rendering) support
  • TypeScript support
  • 1 dependency (use-ssr)
  • Built in state

Examples

Installation

yarn add react-useportal      or     npm i -S react-useportal

Usage

Stateless

import usePortal from 'react-useportal'

const App = () => {
  const { Portal } = usePortal()

  return (
    <Portal>
      This text is portaled at the end of document.body!
    </Portal>
  )
}

const App = () => {
  const { Portal } = usePortal({
    bindTo: document && document.getElementById('san-francisco')
  })

  return (
    <Portal>
      This text is portaled into San Francisco!
    </Portal>
  )
}

With State

import usePortal from 'react-useportal'

const App = () => {
  var { openPortal, closePortal, isOpen, Portal } = usePortal()

  // want to use array destructuring? You can do that too
  var [openPortal, closePortal, isOpen, Portal] = usePortal()

  return (
    <>
      <button onClick={openPortal}>
        Open Portal
      </button>
      {isOpen && (
        <Portal>
          <p>
            This Portal handles its own state.{' '}
            <button onClick={closePortal}>Close me!</button>, hit ESC or
            click outside of me.
          </p>
        </Portal>
      )}
    </>
  )
}

Need Animations?

import usePortal from 'react-useportal'

const App = () => {
  const { openPortal, closePortal, isOpen, Portal } = usePortal()
  return (
    <>
      <button onClick={openPortal}>
        Open Portal
      </button>
      <Portal>
        <p className={isOpen ? 'animateIn' : 'animateOut'}>
          This Portal handles its own state.{' '}
          <button onClick={closePortal}>Close me!</button>, hit ESC or
          click outside of me.
        </p>
      </Portal>
    </>
  )
}

Customizing the Portal directly

By using onOpen, onClose or any other event handler, you can modify the Portal and return it. See useDropdown for a working example. If opening the portal from a click event it's important that you pass the event object to openPortal and togglePortal otherwise you will need to attach a ref to the clicked element (if you want to be able to open the portal without passing an event you will need to set programmaticallyOpen to true).

const useModal = () => {
  const { isOpen, openPortal, togglePortal, closePortal, Portal } = usePortal({
    onOpen({ portal }) {
      portal.current.style.cssText = `
        /* add your css here for the Portal */
        position: fixed;
        left: 50%;
        top: 50%;
        transform: translate(-50%,-50%);
        z-index: 1000;
      `
    }
  })

  return {
    Modal: Portal,
    openModal: openPortal,
    toggleModal: togglePortal,
    closeModal: closePortal,
    isOpen
  }
}

const App = () => {
  const { openModal, closeModal, isOpen, Modal } = useModal()
  
  return <>
    <button onClick={e => openModal(e)}>Open Modal<button>
    {isOpen && (
      <Modal>
        This will dynamically center to the middle of the screen regardless of the size of what you put in here
      </Modal>
    )}
  </>
}

Make sure you are passing the html synthetic event to the openPortal and togglePortal . i.e. onClick={e => openPortal(e)}

Usage with a ref

If for some reason, you don't want to pass around the event to openPortal or togglePortal and you're not using programmaticallyOpen, you can use a ref like this.

import usePortal from 'react-useportal'

const App = () => {
  var { ref, openPortal, closePortal, isOpen, Portal } = usePortal()

  return (
    <>
      {/* see below how I don't have to pass the event if I use the ref */}
      <button ref={ref} onClick={() => openPortal()}>
        Open Portal
      </button>
      {isOpen && (
        <Portal>
          <p>
            This Portal handles its own state.{' '}
            <button onClick={closePortal}>Close me!</button>, hit ESC or
            click outside of me.
          </p>
        </Portal>
      )}
    </>
  )
}

Options

Option Description
closeOnOutsideClick This will close the portal when not clicking within the portal. Default is true
closeOnEsc This will allow you to hit ESC and it will close the modal. Default is true
bindTo This is the DOM node you want to attach the portal to. By default it attaches to document.body
isOpen This will be the default for the portal. Default is false
onOpen This is used to call something when the portal is opened and to modify the css of the portal directly
onClose This is used to call something when the portal is closed and to modify the css of the portal directly
onPortalClick This is fired whenever clicking on the Portal
html event handlers (i.e. onClick) These can be used instead of onOpen to modify the css of the portal directly. onMouseEnter and onMouseLeave example
programmaticallyOpen This option allows you to open or toggle the portal without passing in an event. Default is false

Option Usage

const {
  openPortal,
  closePortal,
  togglePortal,
  isOpen,
  Portal,
  // if you don't pass an event to openPortal, closePortal, or togglePortal and you're not using programmaticallyOpen, you will need
  // to put this on the element you want to interact with/click
  ref,
  // if for some reason you want to interact directly with the portal, you can with this ref
  portalRef,
} = usePortal({
  closeOnOutsideClick: true,
  closeOnEsc: true,
  bindTo, // attach the portal to this node in the DOM
  isOpen: false,
  // `event` has all the fields that a normal `event` would have such as `event.target.value`, etc.
  // with the additional `portal` and `targetEl` added to it as seen in the examples below
  onOpen: (event) => {
    // can access: event.portal, event.targetEl, event.event, event.target, etc.
  },
  // `onClose` will not have an `event` unless you pass an `event` to `closePortal`
  onClose({ portal, targetEl, event }) {},
  // `targetEl` is the element that you either are attaching a `ref` to
  // or that you are putting `openPortal` or `togglePortal` or `closePortal` on
  onPortalClick({ portal, targetEl, event }) {},
  // in addition, any event handler such as onClick, onMouseOver, etc will be handled the same
  onClick({ portal, targetEl, event }) {}
})

Todos

  • React Native support. 1 2 3 4 5 Probably going to have to add a Provider...
  • add correct typescript return types
  • add support for popup windows resource 1 resource 2. Maybe something like
  const { openPortal, closePortal, isOpen, Portal } = usePortal({
    popup: ['', '', 'width=600,height=400,left=200,top=200']
  })
  // window.open('', '', 'width=600,height=400,left=200,top=200')
  • tests (priority)
  • maybe have a <Provider order={['Portal', 'openPortal']} /> then you can change the order of the array destructuring syntax
  • fix code so maintainability is A
  • set up code climate test coverage
  • optimize badges see awesome badge list
    • add code climate test coverage badge

react-useportal's People

Contributors

alex-cory avatar bondos avatar dependabot[bot] avatar georgesofianosgr avatar greenkeeper[bot] avatar hansott avatar huwshimi avatar kunukn avatar poacher2k avatar twelch 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-useportal's Issues

Trigger openPortal or any action without event

I'm trying to openPortal on route change.
When route changes, I get;
You must either add a ref to the element you are interacting with or pass an event to openPortal(e) or togglePortal(e).

How do I do it without ref or event?

An in-range update of @types/react is breaking the build 🚨

The devDependency @types/react was updated from 16.9.2 to 16.9.3.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@types/react is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ci/circleci: test: Your tests failed on CircleCI (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

SSR is failing

I'm building with Gatsby and I got this error.

  88 |     }, [isServer, handleOutsideMouseClick, handleKeydown, elToMountTo]);
  89 |     const Portal = react_1.useCallback(({ children }) => {
> 90 |         if (portal.current instanceof HTMLElement)
     | ^
  91 |             return react_dom_1.createPortal(children, portal.current);
  92 |         return null;
  93 |     }, []);


  WebpackError: ReferenceError: HTMLElement is not defined
  
  - usePortal.js:90 react_1.useCallback
    node_modules/react-useportal/dist/usePortal.js:90:1

I will create my portal on mount for now but I just wanted to report this.

An in-range update of @types/react is breaking the build 🚨

The devDependency @types/react was updated from 16.9.15 to 16.9.16.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@types/react is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ci/circleci: test: Your tests failed on CircleCI (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

onResize

When resizing the screen, we need to have this handler

Sequencing multiple portals doesn't work

I'm trying to achieve the following:

  1. Button is clicked to open the first modal.
  2. In this modal, there is another button. When clicked, it should close the (first) modal, and open a second modal.

The first modal is closed as expected, but the second modal is not opened. Its isOpen is always false.

Repro: Codesandbox

An in-range update of @types/jest is breaking the build 🚨

The devDependency @types/jest was updated from 24.0.16 to 24.0.17.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@types/jest is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • now: Deployment has failed (error: #ys6n9Nh) (Details).
  • ci/circleci: test: Your tests passed on CircleCI! (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Lot of rerenders?

Hi, see this CodeSandbox: https://codesandbox.io/s/useportal-hdhny

I am trying to put a form in the modal, but after every keypress the input loses focus. Inspecting the DOM I noticed the portal div is rerendered very frequently, almost on every keypress/click. The rerender for some reason really recreates the whole DOM note; when I expand the contents of the portal div, they are collapsed again after the rerender. I have trouble figure out why it is happening, already tried wrapping the component in a React.memo but it didn't prevent the rerenders. I feel it's coming from the Portal itself. Can you figure out what is going on here?

An in-range update of @typescript-eslint/eslint-plugin is breaking the build 🚨

The devDependency @typescript-eslint/eslint-plugin was updated from 2.19.0 to 2.19.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@typescript-eslint/eslint-plugin is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ci/circleci: test: Your tests failed on CircleCI (Details).

Release Notes for v2.19.1

2.19.1 (2020-02-10)

Bug Fixes

  • eslint-plugin: [unbound-method] blacklist a few unbound natives (#1562) (4670aab)
  • typescript-estree: ts returning wrong file with project references (#1575) (4c12dac)
Commits

The new version differs by 5 commits.

  • 1c8f0df chore: publish v2.19.1
  • 4c12dac fix(typescript-estree): ts returning wrong file with project references (#1575)
  • e9cf734 docs(eslint-plugin): fix typo in readme
  • 10d86b1 docs(eslint-plugin): [no-dupe-class-members] fix typo (#1566)
  • 4670aab fix(eslint-plugin): [unbound-method] blacklist a few unbound natives (#1562)

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Conditional rendered elements fire as outside click events

Came across an issue when conditionally rendering elements within a portal. Buttons render and work properly if used individually but when conditionally rendered the onClick events close the portal since 'outside' click events. Setting the closeOnOutsideClick: false works fine as a temporary fix but was curious if there was a known solution. Also tried binding e.stopPropagation() in onClick events.

AddTaskCard.js

{isOpen && (
  <>
    <Portal>
      <div className="pointer-events-none fixed w-full h-full top-0 left-0 flex items-center justify-center">
        <div className="modal-overlay absolute w-full h-full bg-gray-800 opacity-50"></div>
      </div>
    </Portal>
    <Portal>
      <AddTaskModal close={closePortal} items={dropItems} />
    </Portal>
  </>
)}

AddTaskModal.js

const [viewAll, setViewAll] = useState(false);

const viewAllItems = () => {
  setViewSize(items.length);
  setViewAll(!viewAll);
};
const resetView = () => {
  setViewSize(3);
  setViewAll(!viewAll);
};
...
<div className="flex justify-end p-3 items-center">
  {/* does not work */}
  {viewAll ? (
    <ResetViewButton onClick={resetView} />
  ) : (
    <ViewAllButton onClick={viewAllItems} />
  )}
  {/* works */}
  <ResetViewButton onClick={resetView} />
  {/* also works */}
  <ViewAllButton onClick={viewAllItems} />
</div>

Can't show modal on page load without opening event

Whenever I want to show modal on render(make it visible without opening event) I get
Warning: Expected server HTML to contain a matching <div> in <div>.

My Code;

const SignInModal = () => {
  var { openPortal, closePortal, isOpen, Portal } = usePortal({
    closeOnEsc: true,
    // bindTo: typeof window !== 'undefined' && document.getElementById('__next'),
  })

  const modalElem = !isOpen && (
    <Portal>
      <ModalBox closeModal={closePortal}>
        <div className="modal__mid1">
          <h4 className="header--medium">Sign In to Walkway</h4>
          <InputText_1>Email or Username</InputText_1>
          <InputText_1>Password</InputText_1>
          <div className="modal__">
            <span>Forgot Password</span>
          </div>
          <div className="modal__mainBtn">
            <div className="modal__mainBtn__line" />
            <TextButton size="18">Sign Up</TextButton>
            <div className="modal__mainBtn__line" />
          </div>
          <span>or</span>
          <div className="modal__">
            <button className="haloOnHov socialMediaBtn">
              <div
                className="socialMediaBtn__icon"
                style={{
                  backgroundImage: `url('../static/icons/fb.svg')`,
                }}
              />
              Sign In with Facebook
            </button>
          </div>
          <p>
            Don’t have an account? <span>Register Now</span>
          </p>
        </div>
      </ModalBox>
    </Portal>
  )

  // return modalElem
  return {
    SignIn_modal: () => modalElem,
    openPortal,
    isOpen,
  }
}

Performance: Many calls to ReactDOM.createPortal() can be slow, can createPortal() be delayed until opened?

Hi, I love this hook! Works well in most cases for things like tooltips.

I've found that if many instances of this hook are created at once (.e.g 50 or more), they all immediately make their own ReactDOM portals, but all those calls to ReactDOM.createPortal() are quite slow and there is a noticeable lag. It got me thinking if there could be an option added to tell this hook to lazily create its ReactDOM portals, so they only get made when they are opened, and persist from then on?

Of course this option would only make sense when used with stateful usages of the hook, because the hook does not know whether its is being used in a stateful way or not.

Another option for me is to create a single tooltip and share that around. Sometimes this is the right solution, but a downside of this approach is that I'd need to have something with knowledge of a set of these hook instances, which can work but kind of goes against React's declarative nature. Letting components use this hook wherever and whenever they like would be my preference.

An in-range update of @types/react is breaking the build 🚨

The devDependency @types/react was updated from 16.8.18 to 16.8.19.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@types/react is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ci/circleci: test: Your tests failed on CircleCI (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

[Feature request] Callback when area outside portal is clicked

I want to check if the modal is saved before the user exit the modal also when they click outside the modal.

Is it possible to add a prop e.g. validateOnOutsideMouseClick: ()=>bool or onOutsideMouseClick: ()=> void

const handleOutsideMouseClick = useCallback((e: MouseEvent): void => {
    const containsTarget = (target: HTMLElRef) => target.current.contains(e.target as HTMLElement)
    if (containsTarget(portal) || (e as any).button !== 0 || !open.current || containsTarget(targetEl)) return
    const isValid = validateOnOutsideMouseClick && typeof validateOnOutsideMouseClick === 'function' ? validateOnOutsideMouseClick() : true;
    if (closeOnOutsideClick && isValid) closePortal(e)
  }, [isServer, closePortal, closeOnOutsideClick, portal])
const handleOutsideMouseClick = useCallback((e: MouseEvent): void => {
    const containsTarget = (target: HTMLElRef) => target.current.contains(e.target as HTMLElement)
    if (containsTarget(portal) || (e as any).button !== 0 || !open.current || containsTarget(targetEl)) return
    if (onOutsideMouseClick) onOutsideMouseClick();
    if (closeOnOutsideClick) closePortal(e)
  }, [isServer, closePortal, closeOnOutsideClick, portal])

An in-range update of @typescript-eslint/parser is breaking the build 🚨

The devDependency @typescript-eslint/parser was updated from 2.19.0 to 2.19.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@typescript-eslint/parser is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ci/circleci: test: Your tests failed on CircleCI (Details).

Release Notes for v2.19.1

2.19.1 (2020-02-10)

Bug Fixes

  • eslint-plugin: [unbound-method] blacklist a few unbound natives (#1562) (4670aab)
  • typescript-estree: ts returning wrong file with project references (#1575) (4c12dac)
Commits

The new version differs by 5 commits.

  • 1c8f0df chore: publish v2.19.1
  • 4c12dac fix(typescript-estree): ts returning wrong file with project references (#1575)
  • e9cf734 docs(eslint-plugin): fix typo in readme
  • 10d86b1 docs(eslint-plugin): [no-dupe-class-members] fix typo (#1566)
  • 4670aab fix(eslint-plugin): [unbound-method] blacklist a few unbound natives (#1562)

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Open a modal on another modal

I'm trying to open a modal on another modal using react-useportal. I manage to make it happen, but I'm getting the following error when I am trying to close or click the second modal.

TypeError: Cannot read properties of undefined (reading 'contains')
    at containsTarget (usePortal.js?4dd6:114:50)
    at eval (usePortal.js?4dd6:115:1)
    at HTMLDocument.eval (usePortal.js?4dd6:126:1)
    at HTMLDocument.__trace__ (bugsnag.js?d359:2764:1)

Also when I check the relevant line on the usePortal.js file, I found the below line.
var containsTarget = function (target) { return target.current.contains(e.target); };

How can I fix this error or, is there any proper way to open a modal on another modal using react-useportal. Any kind of help is highly appreciated 🙏

Action required: Greenkeeper could not be activated 🚨

🚨 You need to enable Continuous Integration on Greenkeeper branches of this repository. 🚨

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.

Since we didn’t receive a CI status on the greenkeeper/initial branch, it’s possible that you don’t have CI set up yet. We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.

If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/.

Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please click the 'fix repo' button on account.greenkeeper.io.

External Method to closePortal

I'm currently using React Hooks and stateless functional components.

If I add closePortal to <button onClick , it will always close the portal and invalidate the onSubmit event.
If I add closePortal to a <button onSubmit, the portal doesn't close.
Is there a function that I can call to close the Portal?

Thanks!

Feature request: onFocus even handler

I am using simple implementation of the usePortal and I was wondering if it possible to implement option with onFocus event. At moment usePortal only use onClick event that bind only to one type of event.

<input type="text" onFocus={openPortal} /> 

Docs suggestion

Just a suggestion based upon a gotcha I encountered. When using SSR you get 'document is not defined' (since document is client side only). I modified your example to remedy this.

  const { Portal } = usePortal({
    bindTo: typeof window !== 'undefined' ? document && document.getElementById('selector') : null
  })

Thanks for the package.

Portal closes when using React Select

Hi again! Check out https://codesandbox.io/s/useportal-mcev3 -> when using the popular react-select with default options, the Portal closes when selecting an option through mouse click. It doesn't happen when using the closeMenuOnSelect parameter. Strange thing is it also doesn't happen when selecting an option via keyboard.

Any idea what is happening here?

Need a interface to set bindTo dynamically

bindTo only could set the element which already rendered in the dom
and while we first call the usePortal, the dom element which i wanted to binding to may not rendered in the dom
so may need a interface to set bindTo element dynamcally

Milestones

  1. Styling the Portal directly with props
const useModal = () => {
  const { isOpen, togglePortal, closePortal, Portal } = usePortal()

  const Modal = useCallback(({ style, ...props }) => {
    // this `isOpen` might be stale within here :/
    const styles = !isOpen ? {} : {
      position: 'fixed',
      left: '50%',
      top: '50%',
      transform: 'translate(-50%,-50%)',
      zIndex: 1000,
      ...styles
    }
    return <Portal {...props} style={styles} />
  }, [isOpen])

  return {
    Modal,
    toggleModal: togglePortal,
    closeModal: closePortal,
    isOpen
  }
}
  1. stateless Portal
import { Portal } from 'react-useportal'
  1. react-native support

Warnings when using react-useportal with React 18.x - Plans for updating peerDeps?

I have a sizable project where I used react-useportal in a few places, and now I am working on upgrading it from running React 17.9 to React 18.2.

Are there any plans for verifying this package's functionality on React 18.x and adding the version to the peerDependencies? Right now I am getting some warnings etc about missing peerDependencies because my React version is "too new".

Portal children cannot access the DOM in useEffect / componentDidMount

Describe the bug
Because the the portal element is appended to the DOM in useEffect, on the initial render, the portal is created in a detached DOM node. It's children render and run their useEffects first, and any access to the DOM will not work.

Codesandbox
https://codesandbox.io/s/objective-keldysh-0078v?file=/src/App.js

To Reproduce
Steps to reproduce the behavior:

  1. try to reference the DOM in a child of the Portal within useEffect or componentDidMount (or just checkout the codesandbox)

Expected behavior
The Portal doesn't render the children until it has attached the root element to the DOM

onScroll Event doesn't fires

I just start using hook portals, which is working great for me. However, there an issue that I found with my setup is that onScroll does not fired by react-useportal. Per documentation we can add any html event handlers, but it doesnt fire as needed. For example:

const DropdownButton = (
  {}
) => {
  const onOpen = ({ event, portal, targetEl }) => {
    console.log(event, portal, targetEl)
    const { top, left } = targetEl.getBoundingClientRect();

    portal.current.style.cssText = `
        position: absolute;
        top: ${top - portal.current.offsetHeight - 10}px;
        left: ${left}px;
      `;
  };

  const {
    ref,
    togglePortal,
    isOpen,
    Portal
  } = usePortal({
    onOpen,
    onScroll: ({ event, portal, targetEl }) => {
      console.log(event, portal, targetEl)
    },
    onWheel: ({ event, portal, targetEl }) => {
      console.log(event, portal, targetEl)
    },
  });

  return (
    <>
      <button
        ref={ref}
        onClick={e => togglePortal(e)}
      >
        test
      </button>
      {isOpen && (
        <Portal>test1</Portal>
      )}
    </>
  );
};

export default DropdownButton;

My goal is to update the position of the dropdown when user start scrolling in the window, so it doesnt float in the screen by itself. Something similar does popover which fires event when we start scrolling in the window.

Feature request: listener onOpen/onClose

In some cases you want to handle open/closed state management from a parent component (for instance, if you want to close the modal after some data has been saved). I can open/close the portal via a prop by doing something like this in the component:

  const { openPortal, closePortal, isOpen, Portal } = usePortal({ isOpen: props.open })

  useEffect( () => {
    if ( props.open === false && isOpen ) closePortal()
    if ( props.open && !isOpen ) openPortal()
  }, [ props.open, isOpen ] )

And it would be nice to also have an onOpen/onClose to respond to closing the portal via ESC or clicking outside the modal.

Hope it's clear. Thanks!

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.