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

Sign up
Appearance settings

Commit b06b99d

Browse files
committed
feat(CDropdown): add reference prop for custom positioning targets
1 parent 9a8af53 commit b06b99d

File tree

4 files changed

+94
-16
lines changed

4 files changed

+94
-16
lines changed

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

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import type { Placements } from '../../types'
2121
import { getNextActiveElement, isRTL } from '../../utils'
2222

2323
import type { Alignments, Directions } from './types'
24-
import { getPlacement } from './utils'
24+
import { getPlacement,getReferenceElement } from './utils'
2525
import { CFocusTrap } from '../focus-trap'
2626

2727
export interface CDropdownProps extends HTMLAttributes<HTMLDivElement | HTMLLIElement> {
@@ -161,6 +161,27 @@ export interface CDropdownProps extends HTMLAttributes<HTMLDivElement | HTMLLIEl
161161
*/
162162
portal?: boolean
163163

164+
/**
165+
* Sets the reference element for positioning the React Dropdown Menu.
166+
* - `toggle` - The React Dropdown Toggle button (default).
167+
* - `parent` - The React Dropdown wrapper element.
168+
* - `HTMLElement` - A custom HTML element.
169+
* - `React.RefObject` - A custom reference element.
170+
*
171+
* @example
172+
* // Use the parent element as reference for positioning
173+
* <CDropdown reference="parent">
174+
* <CDropdownToggle>Toggle dropdown</CDropdownToggle>
175+
* <CDropdownMenu>
176+
* <CDropdownItem>Action</CDropdownItem>
177+
* <CDropdownItem>Another Action</CDropdownItem>
178+
* </CDropdownMenu>
179+
* </CDropdown>
180+
*
181+
* @since 5.9.0
182+
*/
183+
reference?: 'parent' | 'toggle' | HTMLElement | React.RefObject<HTMLElement | null>
184+
164185
/**
165186
* Defines the visual variant of the React Dropdown
166187
*/
@@ -204,6 +225,7 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
204225
popper = true,
205226
popperConfig,
206227
portal = false,
228+
reference = 'toggle',
207229
variant = 'btn-group',
208230
visible = false,
209231
...rest
@@ -255,12 +277,12 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
255277
}, [visible])
256278

257279
useEffect(() => {
258-
const toggleElement = dropdownToggleElement
280+
const referenceElement = getReferenceElement(reference,dropdownToggleElement,dropdownRef)
259281
const menuElement = dropdownMenuRef.current
260-
if (allowPopperUse && menuElement && toggleElement && _visible) {
261-
initPopper(toggleElement, menuElement, computedPopperConfig)
282+
if (allowPopperUse && menuElement && referenceElement && _visible) {
283+
initPopper(referenceElement, menuElement, computedPopperConfig)
262284
}
263-
}, [dropdownToggleElement])
285+
}, [dropdownToggleElement,reference])
264286

265287
useEffect(() => {
266288
if (pendingKeyDownEvent !== null) {
@@ -272,21 +294,21 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
272294
const handleHide = useCallback(() => {
273295
setVisible(false)
274296

275-
const toggleElement = dropdownToggleElement
276297
const menuElement = dropdownMenuRef.current
298+
const toggleElement = dropdownToggleElement
277299

278300
if (allowPopperUse) {
279301
destroyPopper()
280302
}
281303

282-
toggleElement?.removeEventListener('keydown', handleKeydown)
283304
menuElement?.removeEventListener('keydown', handleKeydown)
305+
toggleElement?.removeEventListener('keydown', handleKeydown)
284306

285307
window.removeEventListener('click', handleClick)
286308
window.removeEventListener('keyup', handleKeyup)
287309

288310
onHide?.()
289-
}, [dropdownToggleElement,allowPopperUse, destroyPopper, onHide])
311+
}, [allowPopperUse,dropdownToggleElement, destroyPopper, onHide])
290312

291313
const handleKeydown = useCallback((event: KeyboardEvent) => {
292314
if (!dropdownMenuRef.current) {
@@ -316,7 +338,7 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
316338
dropdownToggleElement?.focus()
317339
}
318340
},
319-
[autoClose, handleHide]
341+
[autoClose, dropdownToggleElement,handleHide]
320342
)
321343

322344
const handleClick = useCallback(
@@ -357,14 +379,15 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
357379

358380
const handleShow = useCallback(
359381
(event?: KeyboardEvent) => {
360-
const toggleElement = dropdownToggleElement
361382
const menuElement = dropdownMenuRef.current
383+
const referenceElement = getReferenceElement(reference, dropdownToggleElement, dropdownRef)
384+
const toggleElement = dropdownToggleElement
362385

363-
if (toggleElement && menuElement) {
386+
if (menuElement && referenceElement&&toggleElement) {
364387
setVisible(true)
365388

366389
if (allowPopperUse) {
367-
initPopper(toggleElement, menuElement, computedPopperConfig)
390+
initPopper(referenceElement, menuElement, computedPopperConfig)
368391
}
369392

370393
toggleElement.focus()
@@ -382,13 +405,14 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
382405
}
383406
},
384407
[
385-
dropdownToggleElement,
386408
allowPopperUse,
387-
initPopper,
388409
computedPopperConfig,
410+
dropdownToggleElement,
411+
reference,
389412
handleClick,
390413
handleKeydown,
391414
handleKeyup,
415+
initPopper,
392416
onShow,
393417
]
394418
)

‎packages/coreui-react/src/components/dropdown/utils.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import React from 'react'
12
import type { Placement } from '@popperjs/core'
23
import type { Placements } from '../../types'
34
import type { Alignments, Breakpoints } from './types'
@@ -49,3 +50,23 @@ export const getPlacement = (
4950

5051
return _placement
5152
}
53+
54+
export const getReferenceElement = (
55+
reference: 'parent' | 'toggle' | React.RefObject<HTMLElement | null> | HTMLElement,
56+
dropdownToggleElement: HTMLElement | null,
57+
dropdownRef: React.RefObject<HTMLElement | null>
58+
): HTMLElement | null => {
59+
if (reference === 'parent') {
60+
return dropdownRef.current
61+
}
62+
63+
if (reference instanceof HTMLElement) {
64+
return reference
65+
}
66+
67+
if (reference instanceof Object && 'current' in reference) {
68+
return reference.current
69+
}
70+
71+
return dropdownToggleElement
72+
}

‎packages/docs/content/api/CDropdown.api.mdx‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,30 @@ const myContainer = document.getElementById('my-container')
190190
<p>Renders the React Dropdown Menu using a React Portal, allowing it to escape the DOM hierarchy for improved positioning.</p>
191191
</td>
192192
</tr>
193+
<tr id="cdropdown-reference">
194+
<td className="text-primary fw-semibold">reference<a href="#cdropdown-reference" aria-label="CDropdown reference permalink" className="anchor-link after">#</a><span className="badge bg-success">5.9.0+</span></td>
195+
<td><code>{`toggle`}</code></td>
196+
<td><code>{`HTMLElement`}</code>, <code>{`'parent'`}</code>, <code>{`'toggle'`}</code>, <code>{`RefObject\<HTMLElement>`}</code></td>
197+
</tr>
198+
<tr>
199+
<td colSpan="3">
200+
<p>Sets the reference element for positioning the React Dropdown Menu.</p>
201+
<ul>
202+
<li><code>{`toggle`}</code> - The React Dropdown Toggle button (default).</li>
203+
<li><code>{`parent`}</code> - The React Dropdown wrapper element.</li>
204+
<li><code>{`HTMLElement`}</code> - A custom HTML element.</li>
205+
<li><code>{`React.RefObject`}</code> - A custom reference element.</li>
206+
</ul>
207+
<JSXDocs code={`// Use the parent element as reference for positioning
208+
<CDropdown reference="parent">
209+
<CDropdownToggle>Toggle dropdown</CDropdownToggle>
210+
<CDropdownMenu>
211+
<CDropdownItem>Action</CDropdownItem>
212+
<CDropdownItem>Another Action</CDropdownItem>
213+
</CDropdownMenu>
214+
</CDropdown>`} />
215+
</td>
216+
</tr>
193217
<tr id="cdropdown-variant">
194218
<td className="text-primary fw-semibold">variant<a href="#cdropdown-variant" aria-label="CDropdown variant permalink" className="anchor-link after">#</a></td>
195219
<td><code>{`btn-group`}</code></td>

‎packages/docs/content/components/dropdown/examples/DropdownOptionsExample.tsx‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react'
2-
import { CDropdown, CDropdownItem, CDropdownMenu, CDropdownToggle } from '@coreui/react'
2+
import { CButton,CDropdown, CDropdownItem, CDropdownMenu, CDropdownToggle } from '@coreui/react'
33

44
export const DropdownOptionsExample = () => {
55
return (
@@ -12,7 +12,7 @@ export const DropdownOptionsExample = () => {
1212
<CDropdownItem href="#">Something else here</CDropdownItem>
1313
</CDropdownMenu>
1414
</CDropdown>
15-
<CDropdown portalvariant="input-group">
15+
<CDropdown portal>
1616
<CDropdownToggle color="secondary" aria-controls="dropdownMenuInPortal">
1717
Portal
1818
</CDropdownToggle>
@@ -22,6 +22,15 @@ export const DropdownOptionsExample = () => {
2222
<CDropdownItem href="#">Something else here</CDropdownItem>
2323
</CDropdownMenu>
2424
</CDropdown>
25+
<CDropdown reference="parent">
26+
<CButton color="secondary">Reference</CButton>
27+
<CDropdownToggle color="secondary" split />
28+
<CDropdownMenu>
29+
<CDropdownItem href="#">Action</CDropdownItem>
30+
<CDropdownItem href="#">Another action</CDropdownItem>
31+
<CDropdownItem href="#">Something else here</CDropdownItem>
32+
</CDropdownMenu>
33+
</CDropdown>
2534
</div>
2635
)
2736
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /