11import React, { FC, ReactNode, useRef, useState } from 'react'
22import { createPortal } from 'react-dom'
3- import PropTypes from 'prop-types'
3+
44import classNames from 'classnames'
5- import { Manager, Popper, Reference } from 'react-popper'
5+ import PropTypes from 'prop-types'
6+ import { usePopper } from 'react-popper'
67import { Transition } from 'react-transition-group'
78
8- // import { CPopoverContent } from './CPopoverContent'
99import { Triggers, triggerPropType } from '../Types'
1010
1111export interface CPopoverProps {
@@ -50,10 +50,10 @@ export interface CPopoverProps {
5050export const CPopover: FC<CPopoverProps> = ({
5151 children,
5252 content,
53- placement = 'top',
5453 offset = [0, 8],
5554 onHide,
5655 onShow,
56+ placement = 'top',
5757 title,
5858 trigger = 'click',
5959 visible,
@@ -62,6 +62,22 @@ export const CPopover: FC<CPopoverProps> = ({
6262 const [_visible, setVisible] = useState(visible)
6363 const popoverRef = useRef()
6464
65+ const [referenceElement, setReferenceElement] = useState(null)
66+ const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
67+ const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null)
68+ const { styles, attributes } = usePopper(referenceElement, popperElement, {
69+ modifiers: [
70+ { name: 'arrow', options: { element: arrowElement } },
71+ {
72+ name: 'offset',
73+ options: {
74+ offset: offset,
75+ },
76+ },
77+ ],
78+ placement: placement,
79+ })
80+
6581 const getTransitionClass = (state: string) => {
6682 return state === 'entering'
6783 ? 'fade'
@@ -73,94 +89,71 @@ export const CPopover: FC<CPopoverProps> = ({
7389 }
7490
7591 return (
76- <Manager>
77- <>
78- <Reference>
79- {({ ref }) =>
80- React.cloneElement(children, {
81- ref: ref,
82- ...((trigger === 'click' || trigger.includes('click')) && {
83- onClick: () => setVisible(!_visible),
84- }),
85- ...((trigger === 'focus' || trigger.includes('focus')) && {
86- onFocus: () => setVisible(true),
87- onBlur: () => setVisible(false),
88- }),
89- ...((trigger === 'hover' || trigger.includes('hover')) && {
90- onMouseEnter: () => setVisible(true),
91- onMouseLeave: () => setVisible(false),
92- }),
93- })
94- }
95- </Reference>
96- {typeof window !== 'undefined' &&
97- createPortal(
98- <Transition
99- in={_visible}
100- mountOnEnter
101- nodeRef={popoverRef}
102- onEnter={onShow}
103- onExit={onHide}
104- timeout={{
105- enter: 0,
106- exit: 200,
107- }}
108- unmountOnExit
109- >
110- {(state) => {
111- const transitionClass = getTransitionClass(state)
112- return (
113- <Popper
114- placement={placement}
115- modifiers={[
116- {
117- name: 'offset',
118- options: {
119- offset: offset,
120- },
121- },
122- ]}
123- >
124- {({ arrowProps, style, ref }) => (
125- <div
126- className={classNames(
127- `popover bs-popover-${
128- placement === 'left'
129- ? 'start'
130- : placement === 'right'
131- ? 'end'
132- : placement
133- }`,
134- transitionClass,
135- )}
136- ref={ref}
137- role="tooltip"
138- style={style}
139- {...rest}
140- >
141- <div className="popover-arrow" {...arrowProps}></div>
142- <div className="popover-header">{title}</div>
143- <div className="popover-body">{content}</div>
144- </div>
145- )}
146- </Popper>
147- )
148- }}
149- </Transition>,
150- document.body,
151- )}
152- </>
153- </Manager>
92+ <>
93+ {React.cloneElement(children, {
94+ ref: setReferenceElement,
95+ ...((trigger === 'click' || trigger.includes('click')) && {
96+ onClick: () => setVisible(!_visible),
97+ }),
98+ ...((trigger === 'focus' || trigger.includes('focus')) && {
99+ onFocus: () => setVisible(true),
100+ onBlur: () => setVisible(false),
101+ }),
102+ ...((trigger === 'hover' || trigger.includes('hover')) && {
103+ onMouseEnter: () => setVisible(true),
104+ onMouseLeave: () => setVisible(false),
105+ }),
106+ })}
107+ {typeof window !== 'undefined' &&
108+ createPortal(
109+ <Transition
110+ in={_visible}
111+ mountOnEnter
112+ nodeRef={popoverRef}
113+ onEnter={onShow}
114+ onExit={onHide}
115+ timeout={{
116+ enter: 0,
117+ exit: 200,
118+ }}
119+ unmountOnExit
120+ >
121+ {(state) => {
122+ const transitionClass = getTransitionClass(state)
123+ return (
124+ <div
125+ className={classNames(
126+ `popover bs-popover-${
127+ placement === 'left' ? 'start' : placement === 'right' ? 'end' : placement
128+ }`,
129+ transitionClass,
130+ )}
131+ ref={setPopperElement}
132+ role="tooltip"
133+ style={styles.popper}
134+ {...attributes.popper}
135+ {...rest}
136+ >
137+ <div className="popover-arrow" style={styles.arrow} ref={setArrowElement}></div>
138+ <div className="popover-header">{title}</div>
139+ <div className="popover-body">{content}</div>
140+ </div>
141+ )
142+ }}
143+ </Transition>,
144+ document.body,
145+ )}
146+ </>
154147 )
155148}
156149
157150CPopover.propTypes = {
158151 children: PropTypes.any,
159152 content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
160- placement: PropTypes.oneOf(['auto', 'top', 'right', 'bottom', 'left']),
161153 offset: PropTypes.any, // TODO: find good proptype
162154 onHide: PropTypes.func,
163155 onShow: PropTypes.func,
156+ placement: PropTypes.oneOf(['auto', 'top', 'right', 'bottom', 'left']),
164157 title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
165158 trigger: triggerPropType,
166159 visible: PropTypes.bool,
0 commit comments