1
1
import React , { FC , ReactNode , useRef , useState } from 'react'
2
2
import { createPortal } from 'react-dom'
3
- import PropTypes from 'prop-types'
3
+
4
4
import classNames from 'classnames'
5
- import { Manager , Popper , Reference } from 'react-popper'
5
+ import PropTypes from 'prop-types'
6
+ import { usePopper } from 'react-popper'
6
7
import { Transition } from 'react-transition-group'
7
8
8
- // import { CPopoverContent } from './CPopoverContent'
9
9
import { Triggers , triggerPropType } from '../Types'
10
10
11
11
export interface CPopoverProps {
@@ -50,10 +50,10 @@ export interface CPopoverProps {
50
50
export const CPopover : FC < CPopoverProps > = ( {
51
51
children,
52
52
content,
53
- placement = 'top' ,
54
53
offset = [ 0 , 8 ] ,
55
54
onHide,
56
55
onShow,
56
+ placement = 'top' ,
57
57
title,
58
58
trigger = 'click' ,
59
59
visible,
@@ -62,6 +62,22 @@ export const CPopover: FC<CPopoverProps> = ({
62
62
const [ _visible , setVisible ] = useState ( visible )
63
63
const popoverRef = useRef ( )
64
64
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
+
65
81
const getTransitionClass = ( state : string ) => {
66
82
return state === 'entering'
67
83
? 'fade'
@@ -73,94 +89,71 @@ export const CPopover: FC<CPopoverProps> = ({
73
89
}
74
90
75
91
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
+ </ >
154
147
)
155
148
}
156
149
157
150
CPopover . propTypes = {
158
151
children : PropTypes . any ,
159
152
content : PropTypes . oneOfType ( [ PropTypes . string , PropTypes . node ] ) ,
160
- placement : PropTypes . oneOf ( [ 'auto' , 'top' , 'right' , 'bottom' , 'left' ] ) ,
161
153
offset : PropTypes . any , // TODO: find good proptype
162
154
onHide : PropTypes . func ,
163
155
onShow : PropTypes . func ,
156
+ placement : PropTypes . oneOf ( [ 'auto' , 'top' , 'right' , 'bottom' , 'left' ] ) ,
164
157
title : PropTypes . oneOfType ( [ PropTypes . string , PropTypes . node ] ) ,
165
158
trigger : triggerPropType ,
166
159
visible : PropTypes . bool ,
0 commit comments