@coreui/react 4.10.1 → 4.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreui/react",
3
- "version": "4.10.1",
3
+ "version": "4.11.0",
4
4
  "description": "UI Components Library for React.js",
5
5
  "keywords": [
6
6
  "react",
@@ -1,21 +1,45 @@
1
- import React, { FC, ReactNode } from 'react'
1
+ import React, { FC, ReactNode, useEffect, useState } from 'react'
2
2
  import { createPortal } from 'react-dom'
3
3
  import PropTypes from 'prop-types'
4
4
 
5
+ const getContainer = (container?: Element | (() => Element | null) | null) => {
6
+ if (container) {
7
+ return typeof container === 'function' ? container() : container
8
+ }
9
+
10
+ return document.body
11
+ }
12
+
5
13
  export interface CConditionalPortalProps {
6
14
  /**
7
15
  * @ignore
8
16
  */
9
17
  children: ReactNode
18
+ /**
19
+ * An HTML element or function that returns a single element, with `document.body` as the default.
20
+ *
21
+ * @since v4.11.0
22
+ */
23
+ container?: Element | (() => Element | null) | null
10
24
  /**
11
25
  * Render some children into a different part of the DOM
12
26
  */
13
- portal: boolean
27
+ portal: boolean | any
14
28
  }
15
29
 
16
- export const CConditionalPortal: FC<CConditionalPortalProps> = ({ children, portal }) => {
17
- return typeof window !== 'undefined' && portal ? (
18
- createPortal(children, document.body)
30
+ export const CConditionalPortal: FC<CConditionalPortalProps> = ({
31
+ children,
32
+ container,
33
+ portal,
34
+ }) => {
35
+ const [_container, setContainer] = useState<ReturnType<typeof getContainer>>(null)
36
+
37
+ useEffect(() => {
38
+ portal && setContainer(getContainer(container) || document.body)
39
+ }, [container, portal])
40
+
41
+ return typeof window !== 'undefined' && portal && _container ? (
42
+ createPortal(children, _container)
19
43
  ) : (
20
44
  <>{children}</>
21
45
  )
@@ -23,7 +47,8 @@ export const CConditionalPortal: FC<CConditionalPortalProps> = ({ children, port
23
47
 
24
48
  CConditionalPortal.propTypes = {
25
49
  children: PropTypes.node,
26
- portal: PropTypes.bool.isRequired,
50
+ container: PropTypes.any, // HTMLElement
51
+ portal: PropTypes.bool,
27
52
  }
28
53
 
29
54
  CConditionalPortal.displayName = 'CConditionalPortal'
@@ -51,6 +51,12 @@ export interface CDropdownProps extends HTMLAttributes<HTMLDivElement | HTMLLIEl
51
51
  * Component used for the root node. Either a string to use a HTML element or a component.
52
52
  */
53
53
  component?: string | ElementType
54
+ /**
55
+ * Appends the react dropdown menu to a specific element. You can pass an HTML element or function that returns a single element. By default `document.body`.
56
+ *
57
+ * @since v4.11.0
58
+ */
59
+ container?: Element | (() => Element | null) | null
54
60
  /**
55
61
  * Sets a darker color scheme to match a dark navbar.
56
62
  */
@@ -147,6 +153,7 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
147
153
  alignment,
148
154
  autoClose = true,
149
155
  className,
156
+ container,
150
157
  dark,
151
158
  direction,
152
159
  offset = [0, 2],
@@ -179,6 +186,7 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
179
186
 
180
187
  const contextValues = {
181
188
  alignment,
189
+ container,
182
190
  dark,
183
191
  dropdownToggleRef,
184
192
  dropdownMenuRef,
@@ -35,13 +35,13 @@ const alignmentClassNames = (alignment: Alignments) => {
35
35
 
36
36
  export const CDropdownMenu = forwardRef<HTMLDivElement | HTMLUListElement, CDropdownMenuProps>(
37
37
  ({ children, className, component: Component = 'ul', ...rest }, ref) => {
38
- const { alignment, dark, dropdownMenuRef, popper, portal, visible } =
38
+ const { alignment, container, dark, dropdownMenuRef, popper, portal, visible } =
39
39
  useContext(CDropdownContext)
40
40
 
41
41
  const forkedRef = useForkedRef(ref, dropdownMenuRef)
42
42
 
43
43
  return (
44
- <CConditionalPortal portal={portal ?? false}>
44
+ <CConditionalPortal container={container} portal={portal ?? false}>
45
45
  <Component
46
46
  className={classNames(
47
47
  'dropdown-menu',
@@ -1,9 +1,10 @@
1
1
  import React, { forwardRef, HTMLAttributes, ReactNode, useRef, useEffect, useState } from 'react'
2
- import { createPortal } from 'react-dom'
2
+ // import { createPortal } from 'react-dom'
3
3
  import classNames from 'classnames'
4
4
  import PropTypes from 'prop-types'
5
5
  import { Transition } from 'react-transition-group'
6
6
 
7
+ import { CConditionalPortal } from '../conditional-portal'
7
8
  import { useForkedRef, usePopper } from '../../hooks'
8
9
  import { fallbackPlacementsPropType, triggerPropType } from '../../props'
9
10
  import type { Placements, Triggers } from '../../types'
@@ -20,6 +21,12 @@ export interface CPopoverProps extends Omit<HTMLAttributes<HTMLDivElement>, 'tit
20
21
  * A string of all className you want applied to the component.
21
22
  */
22
23
  className?: string
24
+ /**
25
+ * Appends the react popover to a specific element. You can pass an HTML element or function that returns a single element. By default `document.body`.
26
+ *
27
+ * @since v4.11.0
28
+ */
29
+ container?: Element | (() => Element | null) | null
23
30
  /**
24
31
  * Content node for your component.
25
32
  */
@@ -74,6 +81,7 @@ export const CPopover = forwardRef<HTMLDivElement, CPopoverProps>(
74
81
  children,
75
82
  animation = true,
76
83
  className,
84
+ container,
77
85
  content,
78
86
  delay = 0,
79
87
  fallbackPlacements = ['top', 'right', 'bottom', 'left'],
@@ -160,45 +168,43 @@ export const CPopover = forwardRef<HTMLDivElement, CPopoverProps>(
160
168
  onMouseLeave: () => toggleVisible(false),
161
169
  }),
162
170
  })}
163
- {typeof window !== 'undefined' &&
164
- createPortal(
165
- <Transition
166
- in={_visible}
167
- mountOnEnter
168
- nodeRef={popoverRef}
169
- onEnter={onShow}
170
- onExit={onHide}
171
- timeout={{
172
- enter: 0,
173
- exit: popoverRef.current
174
- ? getTransitionDurationFromElement(popoverRef.current) + 50
175
- : 200,
176
- }}
177
- unmountOnExit
178
- >
179
- {(state) => (
180
- <div
181
- className={classNames(
182
- 'popover',
183
- 'bs-popover-auto',
184
- {
185
- fade: animation,
186
- show: state === 'entered',
187
- },
188
- className,
189
- )}
190
- ref={forkedRef}
191
- role="tooltip"
192
- {...rest}
193
- >
194
- <div className="popover-arrow"></div>
195
- <div className="popover-header">{title}</div>
196
- <div className="popover-body">{content}</div>
197
- </div>
198
- )}
199
- </Transition>,
200
- document.body,
201
- )}
171
+ <CConditionalPortal container={container} portal={true}>
172
+ <Transition
173
+ in={_visible}
174
+ mountOnEnter
175
+ nodeRef={popoverRef}
176
+ onEnter={onShow}
177
+ onExit={onHide}
178
+ timeout={{
179
+ enter: 0,
180
+ exit: popoverRef.current
181
+ ? getTransitionDurationFromElement(popoverRef.current) + 50
182
+ : 200,
183
+ }}
184
+ unmountOnExit
185
+ >
186
+ {(state) => (
187
+ <div
188
+ className={classNames(
189
+ 'popover',
190
+ 'bs-popover-auto',
191
+ {
192
+ fade: animation,
193
+ show: state === 'entered',
194
+ },
195
+ className,
196
+ )}
197
+ ref={forkedRef}
198
+ role="tooltip"
199
+ {...rest}
200
+ >
201
+ <div className="popover-arrow"></div>
202
+ <div className="popover-header">{title}</div>
203
+ <div className="popover-body">{content}</div>
204
+ </div>
205
+ )}
206
+ </Transition>
207
+ </CConditionalPortal>
202
208
  </>
203
209
  )
204
210
  },
@@ -208,6 +214,7 @@ CPopover.propTypes = {
208
214
  animation: PropTypes.bool,
209
215
  children: PropTypes.node,
210
216
  className: PropTypes.string,
217
+ container: PropTypes.any,
211
218
  content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
212
219
  delay: PropTypes.oneOfType([
213
220
  PropTypes.number,
@@ -1,16 +1,9 @@
1
- import React, {
2
- forwardRef,
3
- HTMLAttributes,
4
- ReactNode,
5
- useRef,
6
- useEffect,
7
- useState,
8
- } from 'react'
9
- import { createPortal } from 'react-dom'
1
+ import React, { forwardRef, HTMLAttributes, ReactNode, useRef, useEffect, useState } from 'react'
10
2
  import classNames from 'classnames'
11
3
  import PropTypes from 'prop-types'
12
4
  import { Transition } from 'react-transition-group'
13
5
 
6
+ import { CConditionalPortal } from '../conditional-portal'
14
7
  import { useForkedRef, usePopper } from '../../hooks'
15
8
  import { fallbackPlacementsPropType, triggerPropType } from '../../props'
16
9
  import type { Placements, Triggers } from '../../types'
@@ -27,6 +20,12 @@ export interface CTooltipProps extends Omit<HTMLAttributes<HTMLDivElement>, 'con
27
20
  * A string of all className you want applied to the component.
28
21
  */
29
22
  className?: string
23
+ /**
24
+ * Appends the react tooltip to a specific element. You can pass an HTML element or function that returns a single element. By default `document.body`.
25
+ *
26
+ * @since v4.11.0
27
+ */
28
+ container?: Element | (() => Element | null) | null
30
29
  /**
31
30
  * Content node for your component.
32
31
  */
@@ -77,6 +76,7 @@ export const CTooltip = forwardRef<HTMLDivElement, CTooltipProps>(
77
76
  children,
78
77
  animation = true,
79
78
  className,
79
+ container,
80
80
  content,
81
81
  delay = 0,
82
82
  fallbackPlacements = ['top', 'right', 'bottom', 'left'],
@@ -162,44 +162,42 @@ export const CTooltip = forwardRef<HTMLDivElement, CTooltipProps>(
162
162
  onMouseLeave: () => toggleVisible(false),
163
163
  }),
164
164
  })}
165
- {typeof window !== 'undefined' &&
166
- createPortal(
167
- <Transition
168
- in={_visible}
169
- mountOnEnter
170
- nodeRef={tooltipRef}
171
- onEnter={onShow}
172
- onExit={onHide}
173
- timeout={{
174
- enter: 0,
175
- exit: tooltipRef.current
176
- ? getTransitionDurationFromElement(tooltipRef.current) + 50
177
- : 200,
178
- }}
179
- unmountOnExit
180
- >
181
- {(state) => (
182
- <div
183
- className={classNames(
184
- 'tooltip',
185
- 'bs-tooltip-auto',
186
- {
187
- fade: animation,
188
- show: state === 'entered',
189
- },
190
- className,
191
- )}
192
- ref={forkedRef}
193
- role="tooltip"
194
- {...rest}
195
- >
196
- <div className="tooltip-arrow"></div>
197
- <div className="tooltip-inner">{content}</div>
198
- </div>
199
- )}
200
- </Transition>,
201
- document.body,
202
- )}
165
+ <CConditionalPortal container={container} portal={true}>
166
+ <Transition
167
+ in={_visible}
168
+ mountOnEnter
169
+ nodeRef={tooltipRef}
170
+ onEnter={onShow}
171
+ onExit={onHide}
172
+ timeout={{
173
+ enter: 0,
174
+ exit: tooltipRef.current
175
+ ? getTransitionDurationFromElement(tooltipRef.current) + 50
176
+ : 200,
177
+ }}
178
+ unmountOnExit
179
+ >
180
+ {(state) => (
181
+ <div
182
+ className={classNames(
183
+ 'tooltip',
184
+ 'bs-tooltip-auto',
185
+ {
186
+ fade: animation,
187
+ show: state === 'entered',
188
+ },
189
+ className,
190
+ )}
191
+ ref={forkedRef}
192
+ role="tooltip"
193
+ {...rest}
194
+ >
195
+ <div className="tooltip-arrow"></div>
196
+ <div className="tooltip-inner">{content}</div>
197
+ </div>
198
+ )}
199
+ </Transition>
200
+ </CConditionalPortal>
203
201
  </>
204
202
  )
205
203
  },
@@ -208,6 +206,7 @@ export const CTooltip = forwardRef<HTMLDivElement, CTooltipProps>(
208
206
  CTooltip.propTypes = {
209
207
  animation: PropTypes.bool,
210
208
  children: PropTypes.node,
209
+ container: PropTypes.any,
211
210
  content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
212
211
  delay: PropTypes.oneOfType([
213
212
  PropTypes.number,