Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7c91c99

Browse filesBrowse files
committed
feat(CDropdown, CPopover, CTooltip): allow passing custom popper configuration
1 parent cb1ef1f commit 7c91c99
Copy full SHA for 7c91c99

File tree

7 files changed

+506
-158
lines changed
Filter options

7 files changed

+506
-158
lines changed

‎packages/coreui-react/src/components/dropdown/CDropdown.tsx

Copy file name to clipboardExpand all lines: packages/coreui-react/src/components/dropdown/CDropdown.tsx
+106-26Lines changed: 106 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import React, {
1010
} from 'react'
1111
import PropTypes from 'prop-types'
1212
import classNames from 'classnames'
13+
import type { Options } from '@popperjs/core'
1314

1415
import { PolymorphicRefForwardingComponent } from '../../helpers'
1516
import { useForkedRef, usePopper } from '../../hooks'
@@ -22,77 +23,148 @@ import { getPlacement } from './utils'
2223

2324
export interface CDropdownProps extends HTMLAttributes<HTMLDivElement | HTMLLIElement> {
2425
/**
25-
* Set aligment of dropdown menu.
26+
* Specifies the alignment of the React Dropdown Menu within this React Dropdown.
27+
*
28+
* @example
29+
* // Align dropdown menu to the end on large devices, otherwise start
30+
* <CDropdown alignment={{ lg: 'end', xs: 'start' }}>
31+
* <CDropdownToggle>Toggle dropdown</CDropdownToggle>
32+
* <CDropdownMenu>
33+
* <CDropdownItem>Action</CDropdownItem>
34+
* <CDropdownItem>Another Action</CDropdownItem>
35+
* </CDropdownMenu>
36+
* </CDropdown>
2637
*
2738
* @type 'start' | 'end' | { xs: 'start' | 'end' } | { sm: 'start' | 'end' } | { md: 'start' | 'end' } | { lg: 'start' | 'end' } | { xl: 'start' | 'end'} | { xxl: 'start' | 'end'}
2839
*/
2940
alignment?: Alignments
41+
3042
/**
31-
* Component used for the root node. Either a string to use a HTML element or a component.
43+
* Determines the root node component (native HTML element or a custom React component) for the React Dropdown.
3244
*/
3345
as?: ElementType
46+
3447
/**
35-
* Configure the auto close behavior of the dropdown:
36-
* - `true` - the dropdown will be closed by clicking outside or inside the dropdown menu.
37-
* - `false` - the dropdown will be closed by clicking the toggle button and manually calling hide or toggle method. (Also will not be closed by pressing esc key)
38-
* - `'inside'` - the dropdown will be closed (only) by clicking inside the dropdown menu.
39-
* - `'outside'` - the dropdown will be closed (only) by clicking outside the dropdown menu.
48+
* Configures automatic closing behavior for the React Dropdown:
49+
* - `true` - Close on clicks inside or outside of the React Dropdown Menu.
50+
* - `false` - Disable auto-close; manually call `hide` or `toggle` (also not closed by `Escape`).
51+
* - `'inside'` - Close only when clicking inside the React Dropdown Menu.
52+
* - `'outside'` - Close only when clicking outside the React Dropdown Menu.
53+
*
54+
* @example
55+
* // Close only when user clicks outside of the menu
56+
* <CDropdown autoClose="outside" />
4057
*/
4158
autoClose?: 'inside' | 'outside' | boolean
59+
4260
/**
43-
* A string of all className you want applied to the base component.
61+
* Adds custom classes to the React Dropdown root element.
4462
*/
4563
className?: string
64+
4665
/**
47-
* 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`.
66+
* Appends the React Dropdown Menu to a specific element. You can pass an HTML element or a function returning an element. Defaults to `document.body`.
67+
*
68+
* @example
69+
* // Append the menu to a custom container
70+
* const myContainer = document.getElementById('my-container')
71+
*
72+
* <CDropdown container={myContainer} />
4873
*
4974
* @since 4.11.0
5075
*/
5176
container?: DocumentFragment | Element | (() => DocumentFragment | Element | null) | null
77+
5278
/**
53-
* Sets a darker color scheme to match a dark navbar.
79+
* Applies a darker color scheme to the React Dropdown Menu, often used within dark navbars.
5480
*/
5581
dark?: boolean
82+
5683
/**
57-
* Sets a specified direction and location of the dropdown menu.
84+
* Specifies the direction of the React Dropdown.
5885
*/
5986
direction?: 'center' | 'dropup' | 'dropup-center' | 'dropend' | 'dropstart'
87+
6088
/**
61-
* Offset of the dropdown menu relative to its target.
89+
* Defines x and y offsets ([x, y]) for the React Dropdown Menu relative to its target.
90+
*
91+
* @example
92+
* // Offset the menu 10px in X and 5px in Y direction
93+
* <CDropdown offset={[10, 5]}>
94+
* ...
95+
* </CDropdown>
6296
*/
6397
offset?: [number, number]
98+
6499
/**
65-
* Callback fired when the component requests to be hidden.
100+
* Callback fired right before the React Dropdown becomes hidden.
66101
*
67102
* @since 4.9.0
68103
*/
69104
onHide?: () => void
105+
70106
/**
71-
* Callback fired when the component requests to be shown.
107+
* Callback fired immediately after the React Dropdown is displayed.
72108
*/
73109
onShow?: () => void
110+
74111
/**
75-
* Describes the placement of your component after Popper.js has applied all the modifiers that may have flipped or altered the originally provided placement property.
112+
* Determines the placement of the React Dropdown Menu after Popper.js modifiers.
76113
*
77-
* @type 'auto' | 'top-end' | 'top' | 'top-start' | 'bottom-end' | 'bottom' | 'bottom-start' | 'right-start' | 'right' | 'right-end' | 'left-start' | 'left' | 'left-end'
114+
* @type 'auto' | 'auto-start' | 'auto-end' | 'top-end' | 'top' | 'top-start' | 'bottom-end' | 'bottom' | 'bottom-start' | 'right-start' | 'right' | 'right-end' | 'left-start' | 'left' | 'left-end'
78115
*/
79116
placement?: Placements
117+
80118
/**
81-
* If you want to disable dynamic positioning set this property to `true`.
119+
* Enables or disables dynamic positioning via Popper.js for the React Dropdown Menu.
82120
*/
83121
popper?: boolean
122+
123+
/**
124+
* Provides a custom Popper.js configuration or a function that returns a modified Popper.js configuration for advanced positioning of the React Dropdown Menu. [Read more](https://popper.js.org/docs/v2/constructors/#options)
125+
*
126+
* @example
127+
* // Providing a custom popper config
128+
* <CDropdown
129+
* popperConfig={{
130+
* modifiers: [
131+
* {
132+
* name: 'flip',
133+
* options: { fallbackPlacements: ['bottom', 'top'] },
134+
* },
135+
* ],
136+
* }}
137+
* >...</CDropdown>
138+
*
139+
* @since 5.5.0
140+
*/
141+
popperConfig?: Partial<Options> | ((defaultPopperConfig: Partial<Options>) => Partial<Options>)
142+
84143
/**
85-
* Generates dropdown menu using createPortal.
144+
* Renders the React Dropdown Menu using a React Portal, allowing it to escape the DOM hierarchy for improved positioning.
86145
*
87146
* @since 4.8.0
88147
*/
89148
portal?: boolean
149+
90150
/**
91-
* Set the dropdown variant to an btn-group, dropdown, input-group, and nav-item.
151+
* Defines the visual variant of the React Dropdown
92152
*/
93153
variant?: 'btn-group' | 'dropdown' | 'input-group' | 'nav-item'
154+
94155
/**
95-
* Toggle the visibility of dropdown menu component.
156+
* Controls the visibility of the React Dropdown Menu:
157+
* - `true` - Visible
158+
* - `false` - Hidden
159+
*
160+
* @example
161+
* // Programmatically manage the dropdown visibility
162+
* const [visible, setVisible] = useState(false)
163+
*
164+
* <CDropdown visible={visible}>
165+
* ...
166+
* </CDropdown>
167+
*
96168
*/
97169
visible?: boolean
98170
}
@@ -126,12 +198,13 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
126198
onShow,
127199
placement = 'bottom-start',
128200
popper = true,
201+
popperConfig,
129202
portal = false,
130203
variant = 'btn-group',
131204
visible = false,
132205
...rest
133206
},
134-
ref,
207+
ref
135208
) => {
136209
const dropdownRef = useRef<HTMLDivElement>(null)
137210
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -161,7 +234,7 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
161234
setVisible,
162235
}
163236

164-
const popperConfig = {
237+
const defaultPopperConfig = {
165238
modifiers: [
166239
{
167240
name: 'offset',
@@ -173,14 +246,20 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
173246
placement: getPlacement(placement, direction, alignment, isRTL(dropdownMenuRef.current)),
174247
}
175248

249+
const computedPopperConfig: Partial<Options> = {
250+
...defaultPopperConfig,
251+
...(typeof popperConfig === 'function' ? popperConfig(defaultPopperConfig) : popperConfig),
252+
}
253+
176254
useEffect(() => {
177255
setVisible(visible)
178256
}, [visible])
179257

180258
useEffect(() => {
181259
if (_visible && dropdownToggleRef.current && dropdownMenuRef.current) {
182260
dropdownToggleRef.current.focus()
183-
popper && initPopper(dropdownToggleRef.current, dropdownMenuRef.current, popperConfig)
261+
popper &&
262+
initPopper(dropdownToggleRef.current, dropdownMenuRef.current, computedPopperConfig)
184263
window.addEventListener('mouseup', handleMouseUp)
185264
window.addEventListener('keyup', handleKeyup)
186265
dropdownToggleRef.current.addEventListener('keydown', handleKeydown)
@@ -209,7 +288,7 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
209288
event.preventDefault()
210289
const target = event.target as HTMLElement
211290
const items: HTMLElement[] = Array.from(
212-
dropdownMenuRef.current.querySelectorAll('.dropdown-item:not(.disabled):not(:disabled)'),
291+
dropdownMenuRef.current.querySelectorAll('.dropdown-item:not(.disabled):not(:disabled)')
213292
)
214293
getNextActiveElement(items, target, event.key === 'ArrowDown', true).focus()
215294
}
@@ -258,7 +337,7 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
258337
[`${direction}`]:
259338
direction && direction !== 'center' && direction !== 'dropup-center',
260339
},
261-
className,
340+
className
262341
)}
263342
{...rest}
264343
ref={forkedRef}
@@ -268,7 +347,7 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
268347
)}
269348
</CDropdownContext.Provider>
270349
)
271-
},
350+
}
272351
)
273352

274353
const alignmentDirection = PropTypes.oneOf<Directions>(['start', 'end'])
@@ -297,6 +376,7 @@ CDropdown.propTypes = {
297376
onShow: PropTypes.func,
298377
placement: placementPropType,
299378
popper: PropTypes.bool,
379+
popperConfig: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
300380
portal: PropTypes.bool,
301381
variant: PropTypes.oneOf(['btn-group', 'dropdown', 'input-group', 'nav-item']),
302382
visible: PropTypes.bool,

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.